pax_global_header00006660000000000000000000000064121761241240014512gustar00rootroot0000000000000052 comment=54b2a1865e18131c4aacd9c079aac69d3eb60b35 tc-play-1.1/000077500000000000000000000000001217612412400127245ustar00rootroot00000000000000tc-play-1.1/.gitignore000066400000000000000000000000441217612412400147120ustar00rootroot00000000000000*~ *.swp *.o *.a *.so *.so.* tcplay tc-play-1.1/CHANGELOG000066400000000000000000000036231217612412400141420ustar00rootroot000000000000002013-07-31 Version 1.1 - Alex Hornung Special thanks to Mike Baker, Joshua Escamilla and Alon Bar-Lev for contributing fixes/improvements! * Added command to request information on mapped volumes. * Added support for full disk encryption. * Added support to query/map volumes using the backup headers instead of the primary headers. * Prefixed dm(4) uuids with CRYPT-TCPLAY-. * Added the 'device' field to all info commands. * Added command to modify/reencrypt a header, allowing passphrase and keyfile changes, as well as restoring from the backup header. * Improvements to Makefile.classic. 2013-05-15 Version 1.0 - Alex Hornung * Fixed bug in hidden volume protection (issue #28). * Added cmake build infrastructure (old single-Makefile option is also still available, using Makefile.classic). * Changed IV gen in mapped devices to plain64 from plain. * Fixed bug in passphrase logic - Truecrypt limits passphrases to 64 bytes (issue #24). * Added unmap option to command line tool (issue #17). * Added option to use /dev/urandom for key material, although this should only ever be used for testing purposes (issue #19). * Complete API overhaul. See man page for details. * Added test framework and tests. * Other minor bugfixes and tweaks. 2012-11-16 Version 0.11 - Alex Hornung Special thanks to Cody Schafer(jmesmon) for providing some of the fixes and to all the people out there packaging tcplay for their distros! If there's any way that I can make your life easier, let me know! Thanks to all the bug reporters as well. * Restored the library/API to working condition * Made it possible to pipe in passwords * Fixed edge case with memcpy copying in and out to the same place * Given up on -Werror, as some Linux distros have warnings even in their standard header files. * Fixed typos tc-play-1.1/CMakeLists.txt000066400000000000000000000133721217612412400154720ustar00rootroot00000000000000cmake_minimum_required (VERSION 2.6) project (tcplay) set (VERSION_MAJOR 1) set (VERSION_MINOR 1) find_package (PkgConfig) include (CheckLibraryExists) set (SRCS_COMMON tcplay.c crc32.c safe_mem.c io.c hdr.c humanize.c crypto.c generic_xts.c) set (SRCS_LINUX crypto-gcrypt.c) set (CFLAGS_LINUX "-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE") set (CFLAGS_WARN "-Wsystem-headers -Wall -W -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wold-style-definition -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wcast-align -Wunused-parameter -Wchar-subscripts -Winline -Wnested-externs") set (CFLAGS_DEBUG "-O0 -g3 -DDEBUG") set (CFLAGS_OPT "-O3") set (CFLAGS_VER "-DMAJ_VER=${VERSION_MAJOR} -DMIN_VER=${VERSION_MINOR}") set (CFLAGS_COMMON "-std=c99 -fPIE ${CFLAGS_LINUX} ${CFLAGS_WARN} ${CFLAGS_VER}") if ($ENV{DEBUG}) set (CFLAGS_COMMON "${CFLAGS_COMMON} ${CFLAGS_DEBUG}") else() set (CFLAGS_COMMON "${CFLAGS_COMMON} ${CFLAGS_OPT}") endif() pkg_check_modules(DEVMAPPER devmapper) pkg_check_modules(UUID uuid) find_library(GCRYPT_LIB gcrypt) if (NOT DEVMAPPER_FOUND) message(FATAL_ERROR "Could not find the devmapper library") endif() if (NOT UUID_FOUND) message(FATAL_ERROR "Could not find the uuid library") endif() if (NOT GCRYPT_LIB) message(FATAL_ERROR "Could not find the gcrypt library") else() set (GCRYPT_LDFLAGS "-lgcrypt -lgpg-error") set (GCRYPT_CFLAGS "") endif() set (CMAKE_REQUIRED_LIBRARIES gpg-error) # gcrypt>=1.5.0 required check_library_exists(gcrypt gcry_kdf_derive "" HAVE_GCRYPT_PBKDF) set (CMAKE_REQUIRED_LIBRARIES "") if (HAVE_GCRYPT_PBKDF) set (SRCS_PBKDF pbkdf2-gcrypt.c) set (LDFLAGS_PBKDF "") set (CFLAGS_PBKDF "") set (PBKDF_BACKEND gcrypt) else() pkg_check_modules(OPENSSL openssl>=1.0.0a) if (NOT OPENSSL_FOUND) message(FATAL_ERROR "Could not find a gcrypt with gcry_kdf_derive() nor OpenSSL >= 1.0.0a") endif() set (SRCS_PBKDF pbkdf2-openssl.c) set (LDFLAGS_PBKDF ${OPENSSL_LDFLAGS}) set (CFLAGS_PBKDF ${OPENSSL_CFLAGS}) set (PBKDF_BACKEND openssl) endif() if (NOT LIB_SUFFIX) message(STATUS "") message(STATUS "LIB_SUFFIX variable is not defined. It will be autodetected now.") message(STATUS "You can set it manually with -DLIB_SUFFIX= (e.g. 64).") if (CMAKE_SIZEOF_VOID_P EQUAL 8) message(STATUS "\nSetting LIB_SUFFIX=64\n") set (LIB_SUFFIX "64") else() message(STATUS "\nSetting LIB_SUFFIX=\n") set (LIB_SUFFIX "") endif() endif() add_executable(tcplay-bin main.c ${SRCS_COMMON} ${SRCS_LINUX} ${SRCS_PBKDF}) set_target_properties(tcplay-bin PROPERTIES OUTPUT_NAME tcplay) set_target_properties(tcplay-bin PROPERTIES COMPILE_FLAGS "${CFLAGS_COMMON} ${DEVMAPPER_CFLAGS} ${UUID_CFLAGS} ${GCRYPT_CFLAGS}") target_link_libraries(tcplay-bin ${DEVMAPPER_LDFLAGS} ${UUID_LDFLAGS} ${GCRYPT_LDFLAGS} ${LDFLAGS_PBKDF}) add_library(tcplay-so SHARED tcplay_api.c ${SRCS_COMMON} ${SRCS_LINUX} ${SRCS_PBKDF}) set_target_properties(tcplay-so PROPERTIES OUTPUT_NAME tcplay) set_target_properties(tcplay-so PROPERTIES SOVERSION ${VERSION_MAJOR}.${VERSION_MINOR}) set_target_properties(tcplay-so PROPERTIES COMPILE_FLAGS "${CFLAGS_COMMON} ${DEVMAPPER_CFLAGS} ${UUID_CFLAGS} ${GCRYPT_CFLAGS}") set_target_properties(tcplay-so PROPERTIES LINK_FLAGS "-Wl,--version-script=${PROJECT_SOURCE_DIR}/tcplay.map") # XXX: revist linking libraries against so. Seems to be more common practice nowadays target_link_libraries(tcplay-so ${DEVMAPPER_LDFLAGS} ${UUID_LDFLAGS} ${GCRYPT_LDFLAGS} ${LDFLAGS_PBKDF}) add_library(tcplay-static STATIC tcplay_api.c ${SRCS_COMMON} ${SRCS_LINUX} ${SRCS_PBKDF}) set_target_properties(tcplay-static PROPERTIES OUTPUT_NAME tcplay) set_target_properties(tcplay-static PROPERTIES VERSION ${VERSION_MAJOR}.${VERSION_MINOR}) set_target_properties(tcplay-static PROPERTIES COMPILE_FLAGS "${CFLAGS_COMMON} ${DEVMAPPER_CFLAGS} ${UUID_CFLAGS} ${GCRYPT_CFLAGS}") # Generate pkg-config file tcplay.pc file(WRITE ${PROJECT_BINARY_DIR}/tcplay.pc "prefix=${CMAKE_INSTALL_PREFIX} exec_prefix=${CMAKE_INSTALL_PREFIX} libdir=${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX} includedir=${CMAKE_INSTALL_PREFIX}/include Name: libtcplay Description: tcplay as a library Version: ${VERSION_MAJOR}.${VERSION_MINOR} Requires.private: devmapper uuid Libs: -L\${libdir} -ltcplay Libs.private: -lgpg-error -lgcrypt Cflags: -I\${includedir} ") # Installation targets install(TARGETS tcplay-bin tcplay-static tcplay-so RUNTIME DESTINATION sbin COMPONENT bin LIBRARY DESTINATION lib${LIB_SUFFIX} COMPONENT lib ARCHIVE DESTINATION lib${LIB_SUFFIX} COMPONENT lib-dev ) install(FILES ${PROJECT_BINARY_DIR}/tcplay.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig COMPONENT lib-dev) install(FILES tcplay_api.h DESTINATION include COMPONENT lib-dev) install(FILES tcplay.3 DESTINATION share/man/man3 COMPONENT lib-dev) install(FILES tcplay.8 DESTINATION share/man/man8 COMPONENT bin) # Optional CPack magic set (CPACK_RPM_COMPONENT_INSTALL 1) set (CPACK_DEB_COMPONENT_INSTALL 1) set (CPACK_PACKAGE_NAME "tcplay") set (CPACK_PACKAGE_CONTACT "Alex Hornung ") set (CPACK_PACKAGE_VENDOR "Alex Hornung ") set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "tcplay is a free (BSD-licensed), pretty much fully featured (including multiple keyfiles, cipher cascades, hidden volumes, etc) and stable TrueCrypt implementation.") set (CPACK_PACAKGE_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}") set (CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}") set (CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}") set (CPACK_PACKAGE_VERSION_PATCH "0") set (CPACK_PACKAGE_LICENSE "2-clause BSD") include(CPack) cpack_add_component(bin DISPLAY_NAME bin REQUIRED INSTALL_TYPES all) cpack_add_component(lib DISPLAY_NAME lib REQUIRED INSTALL_TYPES all) cpack_add_component(lib-dev DISPLAY_NAME lib-dev REQUIRED INSTALL_TYPES all DEPENDS lib) tc-play-1.1/LICENSE000066400000000000000000000024601217612412400137330ustar00rootroot00000000000000Copyright (c) 2011 Alex Hornung . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. tc-play-1.1/Makefile.classic000066400000000000000000000071171217612412400160120ustar00rootroot00000000000000# either linux or dragonfly SYSTEM?=linux DESTDIR?= PREFIX?=/usr/local LIBSUFFIX?= LIBDIR?=$(PREFIX)/lib$(LIBSUFFIX) MANDIR?=$(PREFIX)/share/man SBINDIR?=$(PREFIX)/sbin INCLUDEDIR?=$(PREFIX)/include # either openssl or gcrypt PBKDF_BACKEND?=openssl # system compiler, normally gcc CC?=gcc INSTALL?=install RM?=rm -f LN?=ln # whether to enable debugging or not DEBUG?=no MAJ_VER=1 MIN_VER=1 # I've given up on -Werror for now; many Linux distros have warnings even in # their standard header files. WARNFLAGS= -Wsystem-headers -Wall -W -Wno-unused-parameter \ -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith \ -Wold-style-definition -Wreturn-type -Wcast-qual -Wwrite-strings \ -Wswitch -Wshadow -Wcast-align -Wunused-parameter -Wchar-subscripts \ -Winline -Wnested-externs VER_FLAGS= -DMAJ_VER=$(MAJ_VER) -DMIN_VER=$(MIN_VER) SRCS= tcplay.c crc32.c safe_mem.c io.c hdr.c humanize.c SRCS+= crypto.c generic_xts.c OBJS= tcplay.o crc32.o safe_mem.o io.o hdr.o humanize.o OBJS+= crypto.o generic_xts.o COMMON_CFLAGS= $(WARNFLAGS) -fPIC $(VER_FLAGS) ifeq ($(DEBUG), yes) COMMON_CFLAGS+= -O0 -g -DDEBUG else COMMON_CFLAGS+= -O3 endif ifeq (${SYSTEM}, linux) COMMON_CFLAGS+= -D_GNU_SOURCE LIBS+= -lgcrypt -lgpg-error -ldevmapper -luuid SRCS+= crypto-gcrypt.c OBJS+= crypto-gcrypt.o ifeq (${PBKDF_BACKEND}, gcrypt) SRCS+= pbkdf2-gcrypt.c OBJS+= pbkdf2-gcrypt.o endif ifeq (${PBKDF_BACKEND}, openssl) SRCS+= pbkdf2-openssl.c OBJS+= pbkdf2-openssl.o LIBS+= -lcrypto endif endif ifeq (${SYSTEM}, dragonfly) LIBS+= -lcrypto -ldm -lprop SRCS+= crypto-dev.c OBJS+= crypto-dev.o SRCS+= pbkdf2-openssl.c OBJS+= pbkdf2-openssl.o endif OBJS_PROG= $(OBJS) main.o OBJS_LIB= $(OBJS) tcplay_api.o all: tcplay libtcplay.so libtcplay.a %.o: %.c $(CC) $(COMMON_CFLAGS) $(CFLAGS) -c -o $@ $< tcplay: $(OBJS_PROG) $(CC) $(COMMON_CFLAGS) $(CFLAGS) -o tcplay $(OBJS_PROG) $(LIBS) libtcplay.so.$(MAJ_VER).$(MIN_VER): $(OBJS_LIB) $(CC) -shared -Wl,-version-script=tcplay.map -Wl,-soname=libtcplay.so.$(MAJ_VER).$(MIN_VER) $(LDFLAGS) \ -o libtcplay.so.$(MAJ_VER).$(MIN_VER) $(OBJS_LIB) $(LIBS) libtcplay.so: libtcplay.so.$(MAJ_VER).$(MIN_VER) $(LN) -sf libtcplay.so.$(MAJ_VER).$(MIN_VER) libtcplay.so libtcplay.a: $(OBJS_LIB) $(AR) -rs libtcplay.a $(OBJS_LIB) install: install_program install_lib install_lib_static install_program: tcplay install_man8 $(INSTALL) -d "$(DESTDIR)$(SBINDIR)" $(INSTALL) tcplay "$(DESTDIR)$(SBINDIR)" install_lib: libtcplay.so.$(MAJ_VER).$(MIN_VER) libtcplay.so install_h install_man3 $(INSTALL) -d "$(DESTDIR)$(LIBDIR)" $(INSTALL) libtcplay.so.$(MAJ_VER).$(MIN_VER) "$(DESTDIR)$(LIBDIR)" $(LN) -sf libtcplay.so.$(MAJ_VER).$(MIN_VER) "$(DESTDIR)$(LIBDIR)/libtcplay.so" install_lib_static: libtcplay.a install_h install_man3 $(INSTALL) -d "$(DESTDIR)$(LIBDIR)" $(INSTALL) libtcplay.a "$(DESTDIR)$(LIBDIR)" install_h: tcplay_api.h $(INSTALL) -d "$(DESTDIR)$(INCLUDEDIR)" $(INSTALL) tcplay_api.h "$(DESTDIR)$(INCLUDEDIR)" install_man3: tcplay.3 $(INSTALL) -d "$(DESTDIR)$(MANDIR)/man3" $(INSTALL) tcplay.3 "$(DESTDIR)$(MANDIR)/man3" install_man8: tcplay.8 $(INSTALL) -d "$(DESTDIR)$(MANDIR)/man8" $(INSTALL) tcplay.8 "$(DESTDIR)$(MANDIR)/man8" clean_cmake_mess: $(RM) CMakeCache.txt $(RM) -r CMakeFiles $(RM) CPackConfig.cmake $(RM) CPackSourceConfig.cmake $(RM) Makefile $(RM) cmake_install.cmake $(RM) tcplay.pc clean: clean_cmake_mess $(RM) $(OBJS_PROG) $(RM) $(OBJS_LIB) $(RM) tcplay libtcplay.so* libtcplay.a tcplay.core ktrace.out .PHONY: install install_program install_lib install_lib_static install_man3 install_man8 install_h tc-play-1.1/README.md000066400000000000000000000123201217612412400142010ustar00rootroot00000000000000About ========== tcplay is a free (BSD-licensed), pretty much fully featured (including multiple keyfiles, cipher cascades, etc) and stable TrueCrypt implementation. This implementation supports mapping (opening) both system and normal TrueCrypt volumes, as well as opening hidden volumes and opening an outer volume while protecting a hidden volume. There is also support to create volumes, including hidden volumes, etc. Since version 1.1, there is also support for restoring from the backup header (if present), change passphrase, keyfile and PBKDF2 PRF function. Since tcplay uses dm-crypt (or dm_target_crypt on DragonFly) it makes full use of any available hardware encryption/decryption support once the volume has been mapped. It is based solely on the documentation available on the TrueCrypt website, many hours of trial and error and the output of the Linux' TrueCrypt client. As it turns out, most technical documents on TrueCrypt contain mistakes, hence the trial and error approach. Implementation notes ========== DragonFly BSD uses the hybrid OpenSSL + cryptodev(9) approach that can be found in crypto-dev.c. OpenSSL is only used for the hash/pbkdf2. The encryption/decryption is performed via cryptodev(9) with enabled cryptosoft. On Linux gcrypt is used for the encryption and decryption. For the hash/pbkdf2 either gcrypt or OpenSSL can be used. gcrypt only supports pbkdf2 since its July 2011 release (1.5.0), while OpenSSL has had pbkdf2 since around December 2010, so its easier to find in most distros. The crypto options can be chosen with make/Makefile parameters. Building on Linux is as easy as doing make -f Makefile.classic SYSTEM=linux you can even skip the SYSTEM=linux, since that's the default. To choose the PBKDF backend, you can use either, make -f Makefile.classic PBKDF_BACKEND=openssl or make -f Makefile.classic PBKDF_BACKEND=gcrypt The interface to device mapper is libdevmapper on Linux and libdm on DragonFly. libdm is a BSD-licensed version of libdevmapper that I hacked together in a few hours. On Ubuntu, the following dev packages are needed to build tcplay: apt-get install libdevmapper-dev libgcrypt11-dev uuid-dev cmake ---------- New in version 1.0 is a cmake build system. tcplay can now be built using: mkdir objdir cd objdir cmake .. make NOTE: building inside the source directory is discouraged, so please do build inside an "objdir" directory. If you happen to do it anyway, you can clean up behind cmake using: make -f Makefile.classic clean_cmake_mess This process will check for dependencies and automatically select whether to use OpenSSL or gcrypt as PBKDF backend. In addition, this process will also generate a .pc file (pkg-config) for the tcplay library. The classic single-file Makefile can still be used for building, however, using make -f Makefile.classic Or, if you only want the command line tool: make -f Makefile.classic tcplay Documentation ========== Please refer to the man pages bundled with tcplay. Download for packaging ========== Latest release can be found as a (source) zip at: https://github.com/bwalex/tc-play/archive/v1.0.zip Bugs ========== Please report all bugs on the github issue tracker. If appropriate, please attach a small test volume which you think tcplay isn't handling correctly. The reduce_test_vol.sh script in test/ can significantly reduce the size of a volume when compressed by stripping out all the unnecessary data, leaving only the headers. After that, just bzip2 it and it should be fairly tiny. What would be even better is if you could write a small test case to reproduce the issue. The README in the test/ directory has information on how to write tests for tcplay. OS Support ========== tcplay is now available for both DragonFly BSD and Linux. It is a core part of the DragonFly BSD operating system and is available in a number of linux distros. Licensing ========== The project is under a two-clause BSD license. I would consider dual-licensing it if required. Drop me an email to discuss the options. Development ========== tcplay is pretty much stable, but if you find a bug, please report it. If anyone wants to add new features or port it to another OS, I'll gladly merge your changes into this repository so that there is a single point of contact. I've noticed that sometimes bugs are only reported downstream (e.g. in the distro's bugtracker). Please make sure those bugs are also reported upstream on github, otherwise odds are they will never reach me. Bugs in the TrueCrypt documentation ========== The TrueCrypt documentation is pretty bad and does not really represent the actual on-disk format nor the encryption/decryption process. Some notable differences between actual implementation and documentation: - PBKDF using RIPEMD160 only uses 2000 iterations if the volume isn't a system volume. - The keyfile pool is not XOR'ed with the passphrase but modulo-256 summed. - Every field *except* the minimum version field of the volume header are in big endian. - Some volume header fields (creation time of volume and header) are missing in the documentation. - All two-way cipher cascades are the wrong way round in the documentation, but all three-way cipher cascades are correct. tc-play-1.1/crc32.c000066400000000000000000000125071217612412400140110ustar00rootroot00000000000000/*- * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. * * First, the polynomial itself and its table of feedback terms. The * polynomial is * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 * * Note that we take it "backwards" and put the highest-order term in * the lowest-order bit. The X^32 term is "implied"; the LSB is the * X^31 term, etc. The X^0 term (usually shown as "+1") results in * the MSB being 1 * * Note that the usual hardware shift register implementation, which * is what we're using (we're merely optimizing it by doing eight-bit * chunks at a time) shifts bits into the lowest-order term. In our * implementation, that means shifting towards the right. Why do we * do it this way? Because the calculated CRC must be transmitted in * order from highest-order term to lowest-order term. UARTs transmit * characters in order from LSB to MSB. By storing the CRC this way * we hand it to the UART in the order low-byte to high-byte; the UART * sends each low-bit to hight-bit; and the result is transmission bit * by bit from highest- to lowest-order term without requiring any bit * shuffling on our part. Reception works similarly * * The feedback terms table consists of 256, 32-bit entries. Notes * * The table can be generated at runtime if desired; code to do so * is shown later. It might not be obvious, but the feedback * terms simply represent the results of eight shift/xor opera * tions for all combinations of data and CRC register values * * The values must be right-shifted by eight bits by the "updcrc * logic; the shift must be unsigned (bring in zeroes). On some * hardware you could probably optimize the shift in assembler by * using byte-swap instructions * polynomial $edb88320 */ #include #include #include "crc32.h" uint32_t crc32_tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; uint32_t crc32(const void *buf, size_t size) { const uint8_t *p; uint32_t crc; p = buf; crc = ~0U; while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); return crc ^ ~0U; } uint32_t crc32_intermediate(uint32_t crc, uint8_t d) { return crc32_tab[(crc ^ d) & 0xFF] ^ (crc >> 8); } tc-play-1.1/crc32.h000066400000000000000000000001441217612412400140100ustar00rootroot00000000000000uint32_t crc32(const void *buf, size_t size); uint32_t crc32_intermediate(uint32_t crc, uint8_t d); tc-play-1.1/crypto-dev.c000066400000000000000000000102161217612412400151640ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "tcplay.h" static int getallowsoft(void) { int old; size_t olen; olen = sizeof(old); if (sysctlbyname("kern.cryptodevallowsoft", &old, &olen, NULL, 0) < 0) { perror("accessing sysctl kern.cryptodevallowsoft failed"); } return old; } static void setallowsoft(int new) { int old; size_t olen, nlen; olen = nlen = sizeof(new); if (sysctlbyname("kern.cryptodevallowsoft", &old, &olen, &new, nlen) < 0) { perror("accessing sysctl kern.cryptodevallowsoft failed"); } } static int get_cryptodev_cipher_id(struct tc_crypto_algo *cipher) { if (strcmp(cipher->name, "AES-128-XTS") == 0) return CRYPTO_AES_XTS; else if (strcmp(cipher->name, "AES-256-XTS") == 0) return CRYPTO_AES_XTS; else if (strcmp(cipher->name, "TWOFISH-128-XTS") == 0) return CRYPTO_TWOFISH_XTS; else if (strcmp(cipher->name, "TWOFISH-256-XTS") == 0) return CRYPTO_TWOFISH_XTS; else if (strcmp(cipher->name, "SERPENT-128-XTS") == 0) return CRYPTO_SERPENT_XTS; else if (strcmp(cipher->name, "SERPENT-256-XTS") == 0) return CRYPTO_SERPENT_XTS; else return -1; } int syscrypt(struct tc_crypto_algo *cipher, unsigned char *key, size_t klen, unsigned char *iv, unsigned char *in, unsigned char *out, size_t len, int do_encrypt) { struct session_op session; struct crypt_op cryp; int cipher_id; int cryptodev_fd = -1, fd = -1; cipher_id = get_cryptodev_cipher_id(cipher); if (cipher_id < 0) { tc_log(1, "Cipher %s not found\n", cipher->name); return ENOENT; } if ((cryptodev_fd = open("/dev/crypto", O_RDWR, 0)) < 0) { perror("Could not open /dev/crypto"); goto err; } if (ioctl(cryptodev_fd, CRIOGET, &fd) == -1) { perror("CRIOGET failed"); goto err; } memset(&session, 0, sizeof(session)); session.cipher = cipher_id; session.key = (caddr_t) key; session.keylen = klen; if (ioctl(fd, CIOCGSESSION, &session) == -1) { perror("CIOCGSESSION failed"); goto err; } memset(&cryp, 0, sizeof(cryp)); cryp.ses = session.ses; cryp.op = do_encrypt ? COP_ENCRYPT : COP_DECRYPT; cryp.flags = 0; cryp.len = len; cryp.src = (caddr_t) in; cryp.dst = (caddr_t) out; cryp.iv = (caddr_t) iv; cryp.mac = 0; if (ioctl(fd, CIOCCRYPT, &cryp) == -1) { perror("CIOCCRYPT failed"); goto err; } if (ioctl(fd, CIOCFSESSION, &session.ses) == -1) { perror("CIOCFSESSION failed"); goto err; } close(fd); close(cryptodev_fd); return (0); err: if (fd != -1) close(fd); if (cryptodev_fd != -1) close(cryptodev_fd); return (-1); } int tc_crypto_init(void) { int allowed; allowed = getallowsoft(); if (allowed == 0) setallowsoft(1); return 0; } tc-play-1.1/crypto-gcrypt.c000066400000000000000000000124271217612412400157240ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ //#include #include #include #include /* * Yey for gcrypt and its broken includes... * see http://lists.gnupg.org/pipermail/gcrypt-devel/2011-July/001830.html * and http://seclists.org/wireshark/2011/Jul/208 * for more details... */ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #pragma GCC diagnostic warning "-Wdeprecated-declarations" #include "generic_xts.h" #include "tcplay.h" static int gcrypt_inited = 0; static int gcrypt_encrypt(void *ctx, size_t blk_len, const uint8_t *src, uint8_t *dst) { gcry_cipher_hd_t cipher_hd = (gcry_cipher_hd_t)ctx; gcry_error_t gcry_err; gcry_err = gcry_cipher_encrypt( cipher_hd, dst, blk_len, /* gcry_cipher_get_algo_blklen(GCRY_CIPHER_AES256) */ src, blk_len); return (gcry_err != 0); } static int gcrypt_decrypt(void *ctx, size_t blk_len, const uint8_t *src, uint8_t *dst) { gcry_cipher_hd_t cipher_hd = (gcry_cipher_hd_t)ctx; gcry_error_t gcry_err; gcry_err = gcry_cipher_decrypt( cipher_hd, dst, blk_len /* gcry_cipher_get_algo_blklen(GCRY_CIPHER_AES256) */, src, blk_len); return (gcry_err != 0); } static int gcrypt_set_key(void **ctx, void *arg1, void *arg2 __unused, const u_int8_t *key, int keybits __unused) { gcry_cipher_hd_t *cipher_hd = (gcry_cipher_hd_t *)ctx; int cipher = *((int *)arg1); gcry_error_t gcry_err; gcry_err = gcry_cipher_open( cipher_hd, cipher, GCRY_CIPHER_MODE_ECB, 0); if (gcry_err) return -1; gcry_err = gcry_cipher_setkey( *cipher_hd, key, gcry_cipher_get_algo_keylen(cipher)); if (gcry_err) { gcry_cipher_close(*cipher_hd); *ctx = NULL; return -1; } return 0; } static int gcrypt_zero_key(void **ctx) { gcry_cipher_hd_t *cipher_hd = (gcry_cipher_hd_t *)ctx; if (*cipher_hd == NULL) return 0; gcry_cipher_close(*cipher_hd); return 0; } static int get_gcrypt_cipher_id(struct tc_crypto_algo *cipher) { if (strcmp(cipher->name, "AES-128-XTS") == 0) return GCRY_CIPHER_AES128; else if (strcmp(cipher->name, "AES-256-XTS") == 0) return GCRY_CIPHER_AES256; else if (strcmp(cipher->name, "TWOFISH-128-XTS") == 0) return GCRY_CIPHER_TWOFISH128; else if (strcmp(cipher->name, "TWOFISH-256-XTS") == 0) return GCRY_CIPHER_TWOFISH; /* XXX: really 256? */ else if (strcmp(cipher->name, "SERPENT-128-XTS") == 0) return GCRY_CIPHER_SERPENT128; else if (strcmp(cipher->name, "SERPENT-256-XTS") == 0) return GCRY_CIPHER_SERPENT256; else return -1; } int syscrypt(struct tc_crypto_algo *cipher, unsigned char *key, size_t klen, unsigned char *iv, unsigned char *in, unsigned char *out, size_t len, int do_encrypt) { struct xts_ctx *ctx; int cipher_id; int err; cipher_id = get_gcrypt_cipher_id(cipher); if (cipher_id < 0) { tc_log(1, "Cipher %s not found\n", cipher->name); return ENOENT; } if ((ctx = (struct xts_ctx *)alloc_safe_mem(sizeof(struct xts_ctx))) == NULL) { tc_log(1, "Could not allocate safe xts_xts memory\n"); return ENOMEM; } err = xts_init(ctx, &cipher_id, NULL, gcrypt_set_key, gcrypt_zero_key, gcrypt_encrypt, gcrypt_decrypt, gcry_cipher_get_algo_blklen(cipher_id), key, klen); if (err) { tc_log(1, "Error initializing generic XTS\n"); return EINVAL; } /* When chaining ciphers, we reuse the input buffer as the output buffer */ if (out != in) memcpy(out, in, len); if (do_encrypt) err = xts_encrypt(ctx, out, len, iv); else err = xts_decrypt(ctx, out, len, iv); if (err) { tc_log(1, "Error encrypting/decrypting\n"); xts_uninit(ctx); return EINVAL; } xts_uninit(ctx); free_safe_mem(ctx); return 0; } int tc_crypto_init(void) { if (gcrypt_inited) return 0; gcrypt_inited = 1; gcry_control(GCRYCTL_SUSPEND_SECMEM_WARN); gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0); gcry_control(GCRYCTL_RESUME_SECMEM_WARN); gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); return 0; } tc-play-1.1/crypto.c000066400000000000000000000153461217612412400144210ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "crc32.h" #include "tcplay.h" int tc_cipher_chain_populate_keys(struct tc_cipher_chain *cipher_chain, unsigned char *key) { int total_key_bytes, used_key_bytes; struct tc_cipher_chain *dummy_chain; /* * We need to determine the total key bytes as the key locations * depend on it. */ total_key_bytes = tc_cipher_chain_klen(cipher_chain); /* * Now we need to get prepare the keys, as the keys are in * forward order with respect to the cipher cascade, but * the actual decryption is in reverse cipher cascade order. */ used_key_bytes = 0; for (dummy_chain = cipher_chain; dummy_chain != NULL; dummy_chain = dummy_chain->next) { dummy_chain->key = alloc_safe_mem(dummy_chain->cipher->klen); if (dummy_chain->key == NULL) { tc_log(1, "tc_decrypt: Could not allocate key " "memory\n"); return ENOMEM; } /* XXX: here we assume XTS operation! */ memcpy(dummy_chain->key, key + used_key_bytes/2, dummy_chain->cipher->klen/2); memcpy(dummy_chain->key + dummy_chain->cipher->klen/2, key + (total_key_bytes/2) + used_key_bytes/2, dummy_chain->cipher->klen/2); /* Remember how many key bytes we've seen */ used_key_bytes += dummy_chain->cipher->klen; } return 0; } int tc_cipher_chain_free_keys(struct tc_cipher_chain *cipher_chain) { for (; cipher_chain != NULL; cipher_chain = cipher_chain->next) { if (cipher_chain->key != NULL) { free_safe_mem(cipher_chain->key); cipher_chain->key = NULL; } } return 0; } int tc_encrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key, unsigned char *iv, unsigned char *in, int in_len, unsigned char *out) { struct tc_cipher_chain *chain_start; int err; chain_start = cipher_chain; if ((err = tc_cipher_chain_populate_keys(cipher_chain, key))) return err; #ifdef DEBUG printf("tc_encrypt: starting chain\n"); #endif /* * Now process the actual decryption, in forward cascade order. */ for (; cipher_chain != NULL; cipher_chain = cipher_chain->next) { #ifdef DEBUG printf("tc_encrypt: Currently using cipher %s\n", cipher_chain->cipher->name); #endif err = syscrypt(cipher_chain->cipher, cipher_chain->key, cipher_chain->cipher->klen, iv, in, out, in_len, 1); /* Deallocate this key, since we won't need it anymore */ free_safe_mem(cipher_chain->key); cipher_chain->key = NULL; if (err != 0) { tc_cipher_chain_free_keys(chain_start); return err; } /* Set next input buffer as current output buffer */ in = out; } tc_cipher_chain_free_keys(chain_start); return 0; } int tc_decrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key, unsigned char *iv, unsigned char *in, int in_len, unsigned char *out) { struct tc_cipher_chain *chain_start; int err; chain_start = cipher_chain; if ((err = tc_cipher_chain_populate_keys(cipher_chain, key))) return err; #ifdef DEBUG printf("tc_decrypt: starting chain!\n"); #endif /* * Now process the actual decryption, in reverse cascade order; so * first find the last element in the chain. */ for (; cipher_chain->next != NULL; cipher_chain = cipher_chain->next) ; for (; cipher_chain != NULL; cipher_chain = cipher_chain->prev) { #ifdef DEBUG printf("tc_decrypt: Currently using cipher %s\n", cipher_chain->cipher->name); #endif err = syscrypt(cipher_chain->cipher, cipher_chain->key, cipher_chain->cipher->klen, iv, in, out, in_len, 0); /* Deallocate this key, since we won't need it anymore */ free_safe_mem(cipher_chain->key); cipher_chain->key = NULL; if (err != 0) { tc_cipher_chain_free_keys(chain_start); return err; } /* Set next input buffer as current output buffer */ in = out; } tc_cipher_chain_free_keys(chain_start); return 0; } int apply_keyfiles(unsigned char *pass, size_t pass_memsz, const char *keyfiles[], int nkeyfiles) { int pl, k; unsigned char *kpool; unsigned char *kdata; int kpool_idx; size_t i, kdata_sz; uint32_t crc; if (pass_memsz < MAX_PASSSZ) { tc_log(1, "Not enough memory for password manipluation\n"); return ENOMEM; } pl = strlen((char *)pass); memset(pass+pl, 0, MAX_PASSSZ-pl); if ((kpool = alloc_safe_mem(KPOOL_SZ)) == NULL) { tc_log(1, "Error allocating memory for keyfile pool\n"); return ENOMEM; } memset(kpool, 0, KPOOL_SZ); for (k = 0; k < nkeyfiles; k++) { #ifdef DEBUG printf("Loading keyfile %s into kpool\n", keyfiles[k]); #endif kpool_idx = 0; crc = ~0U; kdata_sz = MAX_KFILE_SZ; if ((kdata = read_to_safe_mem(keyfiles[k], 0, &kdata_sz)) == NULL) { tc_log(1, "Error reading keyfile %s content\n", keyfiles[k]); free_safe_mem(kpool); return EIO; } for (i = 0; i < kdata_sz; i++) { crc = crc32_intermediate(crc, kdata[i]); kpool[kpool_idx++] += (unsigned char)(crc >> 24); kpool[kpool_idx++] += (unsigned char)(crc >> 16); kpool[kpool_idx++] += (unsigned char)(crc >> 8); kpool[kpool_idx++] += (unsigned char)(crc); /* Wrap around */ if (kpool_idx == KPOOL_SZ) kpool_idx = 0; } free_safe_mem(kdata); } #ifdef DEBUG printf("Applying kpool to passphrase\n"); #endif /* Apply keyfile pool to passphrase */ for (i = 0; i < KPOOL_SZ; i++) pass[i] += kpool[i]; free_safe_mem(kpool); return 0; } tc-play-1.1/generic_xts.c000066400000000000000000000072601217612412400154070ustar00rootroot00000000000000/* * Copyright (C) 2008, Damien Miller * Copyright (C) 2011, Alex Hornung * * Permission to use, copy, and modify this software with or without fee * is hereby granted, provided that this entire notice is included in * all copies of any software which is or includes a copy or * modification of this software. * You may use this code under the GNU public license if you so wish. Please * contribute changes back to the authors under this freer than GPL license * so that we may further the use of strong encryption without limitations to * all. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR * PURPOSE. */ #include #include #include #include #include #include "tcplay.h" #include "generic_xts.h" static int xts_reinit(struct xts_ctx *ctx, u_int64_t blocknum) { u_int i; /* * Prepare tweak as E_k2(IV). IV is specified as LE representation * of a 64-bit block number which we allow to be passed in directly. */ for (i = 0; i < XTS_IVSIZE; i++) { ctx->tweak[i] = blocknum & 0xff; blocknum >>= 8; } /* Last 64 bits of IV are always zero */ bzero(ctx->tweak + XTS_IVSIZE, XTS_IVSIZE); return ctx->encrypt_fn(ctx->ctx2, ctx->blk_sz, ctx->tweak, ctx->tweak); } static int xts_crypt(struct xts_ctx *ctx, u_int8_t *data, u_int do_encrypt) { u_int8_t block[XTS_MAX_BLOCKSIZE]; u_int i, carry_in, carry_out; int err; for (i = 0; i < ctx->blk_sz; i++) block[i] = data[i] ^ ctx->tweak[i]; if (do_encrypt) err = ctx->encrypt_fn(ctx->ctx1, ctx->blk_sz, block, data); else err = ctx->decrypt_fn(ctx->ctx1, ctx->blk_sz, block, data); if (err) goto out; for (i = 0; i < ctx->blk_sz; i++) data[i] ^= ctx->tweak[i]; /* Exponentiate tweak */ carry_in = 0; for (i = 0; i < ctx->blk_sz; i++) { carry_out = ctx->tweak[i] & 0x80; ctx->tweak[i] = (ctx->tweak[i] << 1) | (carry_in ? 1 : 0); carry_in = carry_out; } if (carry_in) ctx->tweak[0] ^= XTS_ALPHA; out: bzero(block, sizeof(block)); return err; } int xts_init(struct xts_ctx *ctx, void *arg1, void *arg2, set_key_fn _set_key_fn, zero_key_fn _zero_key_fn, encrypt_decrypt_fn _encrypt_fn, encrypt_decrypt_fn _decrypt_fn, u_int blk_sz, u_int8_t *key, int len) { int err; if (len != 32 && len != 64) return -1; ctx->blk_sz = blk_sz; ctx->encrypt_fn = _encrypt_fn; ctx->decrypt_fn = _decrypt_fn; ctx->set_key_fn = _set_key_fn; ctx->zero_key_fn = _zero_key_fn; err = ctx->set_key_fn(&ctx->ctx1, arg1, arg2, key, len * 4); if (err) return -1; err = ctx->set_key_fn(&ctx->ctx2, arg1, arg2, key + (len / 2), len * 4); if (err) { ctx->zero_key_fn(&ctx->ctx1); return -1; } return 0; } int xts_encrypt(struct xts_ctx *ctx, u_int8_t *data, size_t len, uint8_t *iv) { uint64_t sector = *((uint64_t *)iv); int err; if ((len % ctx->blk_sz) != 0) return -1; err = xts_reinit(ctx, sector); if (err) return err; while (len > 0) { err = xts_crypt(ctx, data, 1); if (err) return -1; data += ctx->blk_sz; len -= ctx->blk_sz; } return err; } int xts_decrypt(struct xts_ctx *ctx, u_int8_t *data, size_t len, uint8_t *iv) { uint64_t sector = *((uint64_t *)iv); int err; if ((len % ctx->blk_sz) != 0) return -1; err = xts_reinit(ctx, sector); if (err) return err; while (len > 0) { err = xts_crypt(ctx, data, 0); if (err) return -1; data += ctx->blk_sz; len -= ctx->blk_sz; } return err; } int xts_uninit(struct xts_ctx *ctx) { ctx->zero_key_fn(&ctx->ctx1); ctx->zero_key_fn(&ctx->ctx2); return 0; } tc-play-1.1/generic_xts.h000066400000000000000000000034041217612412400154100ustar00rootroot00000000000000/* * Copyright (C) 2008, Damien Miller * Copyright (C) 2011, Alex Hornung * * Permission to use, copy, and modify this software with or without fee * is hereby granted, provided that this entire notice is included in * all copies of any software which is or includes a copy or * modification of this software. * You may use this code under the GNU public license if you so wish. Please * contribute changes back to the authors under this freer than GPL license * so that we may further the use of strong encryption without limitations to * all. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR * PURPOSE. */ #include #define XTS_MAX_BLOCKSIZE 16 #define XTS_IVSIZE 8 #define XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ typedef int (*encrypt_decrypt_fn)(void *, size_t, const uint8_t *, uint8_t *); typedef int (*set_key_fn)(void **, void *, void *, const uint8_t *, int); typedef int (*zero_key_fn)(void **); struct xts_ctx { encrypt_decrypt_fn encrypt_fn; encrypt_decrypt_fn decrypt_fn; set_key_fn set_key_fn; zero_key_fn zero_key_fn; void *ctx1; void *ctx2; uint8_t tweak[XTS_MAX_BLOCKSIZE]; uint32_t blk_sz; }; int xts_init(struct xts_ctx *ctxp, void *arg1, void *arg2, set_key_fn set_key_fn, zero_key_fn zero_key_fn, encrypt_decrypt_fn encrypt_fn, encrypt_decrypt_fn decrypt_fn, uint32_t blk_sz, uint8_t *key, int len); int xts_encrypt(struct xts_ctx *ctx, uint8_t *data, size_t len, uint8_t *iv); int xts_decrypt(struct xts_ctx *ctx, uint8_t *data, size_t len, uint8_t *iv); int xts_uninit(struct xts_ctx *ctxp); tc-play-1.1/hdr.c000066400000000000000000000235511217612412400136530ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if defined(__DragonFly__) #include #elif defined(__linux__) #include #endif #include #include #include #include #include #include "crc32.h" #include "tcplay.h" /* Endianess macros */ #define BE_TO_HOST(n, v) v = be ## n ## toh(v) #define LE_TO_HOST(n, v) v = le ## n ## toh(v) #define HOST_TO_BE(n, v) v = htobe ## n (v) #define HOST_TO_LE(n, v) v = htole ## n (v) struct tchdr_dec * decrypt_hdr(struct tchdr_enc *ehdr, struct tc_cipher_chain *cipher_chain, unsigned char *key) { struct tchdr_dec *dhdr; unsigned char iv[128]; int error; if ((dhdr = alloc_safe_mem(sizeof(struct tchdr_dec))) == NULL) { tc_log(1, "Error allocating safe tchdr_dec memory\n"); return NULL; } memset(iv, 0, sizeof(iv)); error = tc_decrypt(cipher_chain, key, iv, ehdr->enc, sizeof(struct tchdr_dec), (unsigned char *)dhdr); if (error) { tc_log(1, "Header decryption failed\n"); free_safe_mem(dhdr); return NULL; } BE_TO_HOST(16, dhdr->tc_ver); LE_TO_HOST(16, dhdr->tc_min_ver); BE_TO_HOST(32, dhdr->crc_keys); BE_TO_HOST(64, dhdr->vol_ctime); BE_TO_HOST(64, dhdr->hdr_ctime); BE_TO_HOST(64, dhdr->sz_hidvol); BE_TO_HOST(64, dhdr->sz_vol); BE_TO_HOST(64, dhdr->off_mk_scope); BE_TO_HOST(64, dhdr->sz_mk_scope); BE_TO_HOST(32, dhdr->flags); BE_TO_HOST(32, dhdr->sec_sz); BE_TO_HOST(32, dhdr->crc_dhdr); return dhdr; } int verify_hdr(struct tchdr_dec *hdr) { uint32_t crc; if (memcmp(hdr->tc_str, TC_SIG, sizeof(hdr->tc_str)) != 0) { #ifdef DEBUG fprintf(stderr, "Signature mismatch\n"); #endif return 0; } crc = crc32((void *)&hdr->keys, 256); if (crc != hdr->crc_keys) { #ifdef DEBUG fprintf(stderr, "CRC32 mismatch (crc_keys)\n"); #endif return 0; } switch(hdr->tc_ver) { case 1: case 2: /* Unsupported header version */ tc_log(1, "Header version %d unsupported\n", hdr->tc_ver); return 0; case 3: case 4: hdr->sec_sz = 512; break; } return 1; } struct tchdr_enc * create_hdr(unsigned char *pass, int passlen, struct pbkdf_prf_algo *prf_algo, struct tc_cipher_chain *cipher_chain, size_t sec_sz, size_t total_blocks __unused, off_t offset, size_t blocks, int hidden, int weak, struct tchdr_enc **backup_hdr) { struct tchdr_enc *ehdr, *ehdr_backup; struct tchdr_dec *dhdr; unsigned char *key, *key_backup; unsigned char iv[128]; int error; key = key_backup = NULL; dhdr = NULL; ehdr = ehdr_backup = NULL; if (backup_hdr != NULL) *backup_hdr = NULL; if ((dhdr = (struct tchdr_dec *)alloc_safe_mem(sizeof(*dhdr))) == NULL) { tc_log(1, "could not allocate safe dhdr memory\n"); goto error; } if ((ehdr = (struct tchdr_enc *)alloc_safe_mem(sizeof(*ehdr))) == NULL) { tc_log(1, "could not allocate safe ehdr memory\n"); goto error; } if ((ehdr_backup = (struct tchdr_enc *)alloc_safe_mem (sizeof(*ehdr_backup))) == NULL) { tc_log(1, "could not allocate safe ehdr_backup memory\n"); goto error; } if ((key = alloc_safe_mem(MAX_KEYSZ)) == NULL) { tc_log(1, "could not allocate safe key memory\n"); goto error; } if ((key_backup = alloc_safe_mem(MAX_KEYSZ)) == NULL) { tc_log(1, "could not allocate safe backup key memory\n"); goto error; } if ((error = get_random(ehdr->salt, sizeof(ehdr->salt), weak)) != 0) { tc_log(1, "could not get salt\n"); goto error; } if ((error = get_random(ehdr_backup->salt, sizeof(ehdr_backup->salt), weak)) != 0) { tc_log(1, "could not get salt for backup header\n"); goto error; } error = pbkdf2(prf_algo, (char *)pass, passlen, ehdr->salt, sizeof(ehdr->salt), MAX_KEYSZ, key); if (error) { tc_log(1, "could not derive key\n"); goto error; } error = pbkdf2(prf_algo, (char *)pass, passlen, ehdr_backup->salt, sizeof(ehdr_backup->salt), MAX_KEYSZ, key_backup); if (error) { tc_log(1, "could not derive backup key\n"); goto error; } memset(dhdr, 0, sizeof(*dhdr)); if ((error = get_random(dhdr->keys, sizeof(dhdr->keys), weak)) != 0) { tc_log(1, "could not get key random bits\n"); goto error; } memcpy(dhdr->tc_str, "TRUE", 4); dhdr->tc_ver = 5; dhdr->tc_min_ver = 7; dhdr->crc_keys = crc32((void *)&dhdr->keys, 256); dhdr->sz_vol = blocks * sec_sz; if (hidden) dhdr->sz_hidvol = dhdr->sz_vol; dhdr->off_mk_scope = offset * sec_sz; dhdr->sz_mk_scope = blocks * sec_sz; dhdr->sec_sz = sec_sz; dhdr->flags = 0; HOST_TO_BE(16, dhdr->tc_ver); HOST_TO_LE(16, dhdr->tc_min_ver); HOST_TO_BE(32, dhdr->crc_keys); HOST_TO_BE(64, dhdr->sz_vol); HOST_TO_BE(64, dhdr->sz_hidvol); HOST_TO_BE(64, dhdr->off_mk_scope); HOST_TO_BE(64, dhdr->sz_mk_scope); HOST_TO_BE(32, dhdr->sec_sz); HOST_TO_BE(32, dhdr->flags); dhdr->crc_dhdr = crc32((void *)dhdr, 188); HOST_TO_BE(32, dhdr->crc_dhdr); memset(iv, 0, sizeof(iv)); error = tc_encrypt(cipher_chain, key, iv, (unsigned char *)dhdr, sizeof(struct tchdr_dec), ehdr->enc); if (error) { tc_log(1, "Header encryption failed\n"); goto error; } memset(iv, 0, sizeof(iv)); error = tc_encrypt(cipher_chain, key_backup, iv, (unsigned char *)dhdr, sizeof(struct tchdr_dec), ehdr_backup->enc); if (error) { tc_log(1, "Backup header encryption failed\n"); goto error; } free_safe_mem(key); free_safe_mem(key_backup); free_safe_mem(dhdr); if (backup_hdr != NULL) *backup_hdr = ehdr_backup; else free_safe_mem(ehdr_backup); return ehdr; /* NOT REACHED */ error: if (key) free_safe_mem(key); if (key_backup) free_safe_mem(key_backup); if (dhdr) free_safe_mem(dhdr); if (ehdr) free_safe_mem(ehdr); if (ehdr_backup) free_safe_mem(ehdr_backup); return NULL; } struct tchdr_enc *copy_reencrypt_hdr(unsigned char *pass, int passlen, struct pbkdf_prf_algo *prf_algo, int weak, struct tcplay_info *info, struct tchdr_enc **backup_hdr) { struct tchdr_enc *ehdr, *ehdr_backup; unsigned char *key, *key_backup; unsigned char iv[128]; int error; key = key_backup = NULL; ehdr = ehdr_backup = NULL; /* By default stick to current PRF algo */ if (prf_algo == NULL) prf_algo = info->pbkdf_prf; if ((ehdr = (struct tchdr_enc *)alloc_safe_mem(sizeof(*ehdr))) == NULL) { tc_log(1, "could not allocate safe ehdr memory\n"); goto error; } if ((ehdr_backup = (struct tchdr_enc *)alloc_safe_mem (sizeof(*ehdr_backup))) == NULL) { tc_log(1, "could not allocate safe ehdr_backup memory\n"); goto error; } if ((key = alloc_safe_mem(MAX_KEYSZ)) == NULL) { tc_log(1, "could not allocate safe key memory\n"); goto error; } if ((key_backup = alloc_safe_mem(MAX_KEYSZ)) == NULL) { tc_log(1, "could not allocate safe backup key memory\n"); goto error; } if ((error = get_random(ehdr->salt, sizeof(ehdr->salt), weak)) != 0) { tc_log(1, "could not get salt\n"); goto error; } if ((error = get_random(ehdr_backup->salt, sizeof(ehdr_backup->salt), weak)) != 0) { tc_log(1, "could not get salt for backup header\n"); goto error; } error = pbkdf2(prf_algo, (char *)pass, passlen, ehdr->salt, sizeof(ehdr->salt), MAX_KEYSZ, key); if (error) { tc_log(1, "could not derive key\n"); goto error; } error = pbkdf2(prf_algo, (char *)pass, passlen, ehdr_backup->salt, sizeof(ehdr_backup->salt), MAX_KEYSZ, key_backup); if (error) { tc_log(1, "could not derive backup key\n"); goto error; } HOST_TO_BE(16, info->hdr->tc_ver); HOST_TO_LE(16, info->hdr->tc_min_ver); HOST_TO_BE(32, info->hdr->crc_keys); HOST_TO_BE(64, info->hdr->vol_ctime); HOST_TO_BE(64, info->hdr->hdr_ctime); HOST_TO_BE(64, info->hdr->sz_vol); HOST_TO_BE(64, info->hdr->sz_hidvol); HOST_TO_BE(64, info->hdr->off_mk_scope); HOST_TO_BE(64, info->hdr->sz_mk_scope); HOST_TO_BE(32, info->hdr->sec_sz); HOST_TO_BE(32, info->hdr->flags); HOST_TO_BE(32, info->hdr->crc_dhdr); memset(iv, 0, sizeof(iv)); error = tc_encrypt(info->cipher_chain, key, iv, (unsigned char *)info->hdr, sizeof(struct tchdr_dec), ehdr->enc); if (error) { tc_log(1, "Header encryption failed\n"); goto error; } memset(iv, 0, sizeof(iv)); error = tc_encrypt(info->cipher_chain, key_backup, iv, (unsigned char *)info->hdr, sizeof(struct tchdr_dec), ehdr_backup->enc); if (error) { tc_log(1, "Backup header encryption failed\n"); goto error; } free_safe_mem(key); free_safe_mem(key_backup); if (backup_hdr != NULL) *backup_hdr = ehdr_backup; else free_safe_mem(ehdr_backup); return ehdr; /* NOT REACHED */ error: if (key) free_safe_mem(key); if (key_backup) free_safe_mem(key_backup); if (ehdr) free_safe_mem(ehdr); if (ehdr_backup) free_safe_mem(ehdr_backup); return NULL; } tc-play-1.1/humanize.c000066400000000000000000000076671217612412400147300ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include "humanize.h" static const char prefixes[] = " KMGTPE"; int _humanize_number(char *buf, size_t bufsz, uint64_t num) { const char *prefixp; int ret; uint64_t i, d; prefixp = prefixes; i = num; d = 0; while ((i > 1024) && (*prefixp != '\0')) { d = (i % 1024)/10; i /= 1024; ++prefixp; } if (d > 0) ret = snprintf(buf, bufsz, "%"PRIu64".%"PRIu64" %c", i, d, *prefixp); else ret = snprintf(buf, bufsz, "%"PRIu64" %c", i, *prefixp); if ((ret < 0) || ((size_t)ret >= bufsz)) { errno = ENOMEM; return -1; } else { return 0; } } int _dehumanize_number(const char *buf, uint64_t *dest) { char *endptr; uint64_t n, n_check, d; uint64_t multiplier; size_t len; if (*buf == '\0') { errno = EINVAL; return -1; } len = strlen(buf); if (tolower(buf[len-1]) == 'b') --len; multiplier = 1; switch (tolower(buf[len-1])) { case 'y': multiplier *= 1024; case 'z': multiplier *= 1024; case 'e': multiplier *= 1024; case 'p': multiplier *= 1024; case 't': multiplier *= 1024; case 'g': multiplier *= 1024; case 'm': multiplier *= 1024; case 'k': multiplier *= 1024; break; default: /* * only set error if string ends in a character that * is not a valid unit. */ if (isalpha(buf[len-1])) { errno = EINVAL; return -1; } } d = 0; n = n_check = strtoull(buf, &endptr, 10); if (endptr) { if ((*endptr != '.') && (*endptr != '\0') && (*endptr != ' ') && (endptr != &buf[len-1])) { errno = EINVAL; return -1; } if (*endptr == '.') { d = strtoull(endptr+1, &endptr, 10); if (endptr && (*endptr != '\0') && (*endptr != ' ') && (endptr != &buf[len-1])) { errno = EINVAL; return -1; } } } if (d != 0) { while (d < 100) d *= 10; while (d > 1000) d /= 10; } d *= (multiplier/1024); n *= multiplier; if ((uint64_t)(n/multiplier) != n_check) { errno = ERANGE; return -1; } n += d; *dest = n; return 0; } #ifdef __TEST_HUMANIZE__ #include int main(int argc, char *argv[]) { char buf[1024]; uint64_t n, out; int err; if (argc < 3) return -1; n = strtoull(argv[1], NULL, 10); err = _humanize_number(buf, 1024, n); assert(err == 0); err = _dehumanize_number(buf, &out); assert(err == 0); printf("Converting: %"PRIu64" => %s => %"PRIu64"\n", n, buf, out); err = _dehumanize_number(argv[2], &out); assert (err == 0); printf("Converting: %s => %"PRIu64"\n", argv[2], out); return 0; } #endif tc-play-1.1/humanize.h000066400000000000000000000027721217612412400147250ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ int _humanize_number(char *buf, size_t bufsz, uint64_t num); int _dehumanize_number(const char *buf, uint64_t *dest); tc-play-1.1/io.c000066400000000000000000000223501217612412400135010ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if defined(__DragonFly__) #include #elif defined(__linux__) #include #include #endif #include #include #include #include #include #include #include #include #include #include #include "tcplay.h" void * read_to_safe_mem(const char *file, off_t offset, size_t *sz) { void *mem = NULL; ssize_t r = 0; int fd; if ((fd = open(file, O_RDONLY)) < 0) { tc_log(1, "Error opening file %s\n", file); return NULL; } if ((mem = alloc_safe_mem(*sz)) == NULL) { tc_log(1, "Error allocating memory\n"); goto out; } if ((lseek(fd, offset, (offset >= 0) ? SEEK_SET : SEEK_END) < 0)) { tc_log(1, "Error seeking on file %s\n", file); goto m_err; } if ((r = read(fd, mem, *sz)) <= 0) { tc_log(1, "Error reading from file %s\n", file); goto m_err; } out: *sz = r; close(fd); return mem; /* NOT REACHED */ m_err: free_safe_mem(mem); close(fd); return NULL; } static size_t get_random_total_bytes = 0; static size_t get_random_read_bytes = 0; float get_random_read_progress(void) { return (get_random_total_bytes == 0) ? 0.0 : (1.0 * get_random_read_bytes) / (1.0 * get_random_total_bytes) * 100.0; } static void get_random_summary(void) { float pct_done = get_random_read_progress(); tc_log(0, "Gathering true randomness, %.0f%% done.\n", pct_done); } int get_random(unsigned char *buf, size_t len, int weak) { int fd; ssize_t r; size_t rd = 0; size_t sz; struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; /* 10 ms */ if ((fd = open((weak) ? "/dev/urandom" : "/dev/random", O_RDONLY)) < 0) { tc_log(1, "Error opening /dev/random\n"); return -1; } summary_fn = get_random_summary; get_random_total_bytes = len; tc_internal_state = STATE_GET_RANDOM; /* Get random data in 16-byte chunks */ sz = 16; while (rd < len) { get_random_read_bytes = rd; if ((len - rd) < sz) sz = (len - rd); if ((r = read(fd, buf+rd, sz)) < 0) { tc_log(1, "Error reading from /dev/random(%d): %s\n", fd, strerror(errno)); close(fd); summary_fn = NULL; tc_internal_state = STATE_UNKNOWN; return -1; } rd += r; nanosleep(&ts, NULL); } close(fd); summary_fn = NULL; tc_internal_state = STATE_UNKNOWN; return 0; } static size_t secure_erase_total_bytes = 0; static size_t secure_erase_erased_bytes = 0; float get_secure_erase_progress(void) { return (secure_erase_total_bytes == 0) ? 0.0 : (1.0 * secure_erase_erased_bytes) / (1.0 * secure_erase_total_bytes) * 100.0; } static void secure_erase_summary(void) { float pct_done = get_secure_erase_progress(); tc_log(0, "Securely erasing, %.0f%% done.\n", pct_done); } int secure_erase(const char *dev, size_t bytes, size_t blksz) { size_t erased = 0; int fd_rand, fd; char buf[ERASE_BUFFER_SIZE]; ssize_t r, w; size_t sz; if (blksz > MAX_BLKSZ) { tc_log(1, "blksz > MAX_BLKSZ\n"); return -1; } if ((fd_rand = open("/dev/urandom", O_RDONLY)) < 0) { tc_log(1, "Error opening /dev/urandom\n"); return -1; } if ((fd = open(dev, O_WRONLY)) < 0) { close(fd_rand); tc_log(1, "Error opening %s\n", dev); return -1; } summary_fn = secure_erase_summary; secure_erase_total_bytes = bytes; tc_internal_state = STATE_ERASE; sz = ERASE_BUFFER_SIZE; while (erased < bytes) { secure_erase_erased_bytes = erased; /* Switch to block size when not much is remaining */ if ((bytes - erased) <= ERASE_BUFFER_SIZE) sz = blksz; if ((r = read(fd_rand, buf, sz)) < 0) { tc_log(1, "Error reading from /dev/urandom\n"); close(fd); close(fd_rand); summary_fn = NULL; tc_internal_state = STATE_UNKNOWN; return -1; } if (r < (ssize_t)blksz) continue; if ((w = write(fd, buf, r)) < 0) { tc_log(1, "Error writing to %s\n", dev); close(fd); close(fd_rand); summary_fn = NULL; tc_internal_state = STATE_UNKNOWN; return -1; } erased += (size_t)w; } close(fd); close(fd_rand); summary_fn = NULL; tc_internal_state = STATE_UNKNOWN; return 0; } #if defined(__DragonFly__) int get_disk_info(const char *dev, size_t *blocks, size_t *bsize) { struct partinfo pinfo; int fd; if ((fd = open(dev, O_RDONLY)) < 0) { tc_log(1, "Error opening %s\n", dev); return -1; } memset(&pinfo, 0, sizeof(struct partinfo)); if (ioctl(fd, DIOCGPART, &pinfo) < 0) { close(fd); return -1; } *blocks = pinfo.media_blocks; *bsize = pinfo.media_blksize; close(fd); return 0; } #elif defined(__linux__) int get_disk_info(const char *dev, size_t *blocks, size_t *bsize) { uint64_t nbytes; int blocksz; int fd; if ((fd = open(dev, O_RDONLY)) < 0) { tc_log(1, "Error opening %s\n", dev); return -1; } if ((ioctl(fd, BLKSSZGET, &blocksz)) < 0) { close(fd); return -1; } if ((ioctl(fd, BLKGETSIZE64, &nbytes)) < 0) { close(fd); return -1; } *blocks = (size_t)(nbytes / blocksz); *bsize = (size_t)(blocksz); close(fd); return 0; } #endif int write_to_disk(const char *dev, off_t offset, size_t blksz, void *mem, size_t bytes) { unsigned char *mem_buf = NULL; ssize_t w; size_t sz; off_t internal_off; int fd; /* Align to block sizes */ internal_off = offset % blksz; #ifdef DEBUG printf("offset: %"PRIu64", internal offset: %"PRIu64"\n", (uint64_t)offset, (uint64_t)internal_off); #endif offset = (offset/blksz) * blksz; if ((internal_off + bytes) > blksz) { tc_log(1, "This should never happen: internal_off + bytes > " "blksz (write_to_disk)\n"); return -1; } if ((bytes < blksz) || (internal_off != 0)) { sz = blksz; if ((mem_buf = read_to_safe_mem(dev, offset, &sz)) == NULL) { tc_log(1, "Error buffering data on " "write_to_disk(%s)\n", dev); return -1; } memcpy(mem_buf + internal_off, mem, bytes); } if ((fd = open(dev, O_WRONLY)) < 0) { tc_log(1, "Error opening device %s\n", dev); return -1; } if ((lseek(fd, offset, (offset >= 0) ? SEEK_SET : SEEK_END) < 0)) { tc_log(1, "Error seeking on device %s\n", dev); close(fd); return -1; } if ((w = write(fd, (mem_buf != NULL) ? mem_buf : mem, bytes)) <= 0) { tc_log(1, "Error writing to device %s\n", dev); close(fd); return -1; } close(fd); if (mem_buf != NULL) free_safe_mem(mem_buf); return 0; } static struct termios termios_old; static int tty_fd; static void sigint_termios(int sa) { tcsetattr(tty_fd, TCSAFLUSH, &termios_old); exit(sa); } int read_passphrase(const char *prompt, char *pass, size_t passlen, size_t bufsz, time_t timeout) { struct termios termios_new; struct timeval to; fd_set fds; ssize_t n; int fd = STDIN_FILENO, r = 0, nready; struct sigaction act, old_act; int is_tty = isatty(fd); if (is_tty == 0) errno = 0; memset(pass, 0, bufsz); printf("%s", prompt); fflush(stdout); /* If input is being provided by something which is not a terminal, don't * change the settings. */ if (is_tty) { tcgetattr(fd, &termios_old); memcpy(&termios_new, &termios_old, sizeof(termios_new)); termios_new.c_lflag &= ~ECHO; act.sa_handler = sigint_termios; act.sa_flags = SA_RESETHAND; sigemptyset(&act.sa_mask); tty_fd = fd; sigaction(SIGINT, &act, &old_act); tcsetattr(fd, TCSAFLUSH, &termios_new); } if (timeout > 0) { memset(&to, 0, sizeof(to)); to.tv_sec = timeout; FD_ZERO(&fds); FD_SET(fd, &fds); nready = select(fd + 1, &fds, NULL, NULL, &to); if (nready <= 0) { r = EINTR; goto out; } } n = read(fd, pass, bufsz-1); if (n > 0) { pass[n-1] = '\0'; /* Strip trailing \n */ } else { r = EIO; } /* Warn about passphrase trimming */ if (strlen(pass) > MAX_PASSSZ) tc_log(0, "WARNING: Passphrase is being trimmed to %zu " "characters, discarding rest.\n", passlen); /* Cut off after passlen characters */ pass[passlen] = '\0'; out: if (is_tty) { tcsetattr(fd, TCSAFLUSH, &termios_old); putchar('\n'); sigaction(SIGINT, &old_act, NULL); } return r; } tc-play-1.1/main.c000066400000000000000000000350401217612412400140160ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "tcplay.h" #ifndef SIGINFO #define SIGINFO SIGUSR1 #endif #define FLAG_LONG_FDE 0xff01 #define FLAG_LONG_USE_BACKUP 0xff02 #define FLAG_LONG_MOD 0xff04 #define FLAG_LONG_MOD_KF 0xff08 #define FLAG_LONG_MOD_PRF 0xff10 #define FLAG_LONG_MOD_NONE 0xff20 static void sig_handler(int sig) { if ((sig == SIGUSR1 || sig == SIGINFO) && (summary_fn != NULL)) summary_fn(); } static void usage(void) { fprintf(stderr, "usage: tcplay -c -d device [-g] [-z] [-w] [-a pbkdf_hash] [-b cipher]\n" " [-f keyfile_hidden] [-k keyfile] [-x pbkdf_hash] [-y cipher]\n" " tcplay -i -d device [-e] [-f keyfile_hidden] [-k keyfile]\n" " [-s system_device] [--fde] [--use-backup]\n" " tcplay -m mapping -d device [-e] [-f keyfile_hidden] [-k keyfile]\n" " [-s system_device] [--fde] [--use-backup]\n" " tcplay --modify -d device [-k keyfile] [--new-keyfile=keyfile]\n" " [--new-pbkdf-prf=pbkdf_hash] [-s system_device] [--fde]\n" " [--use-backup] [-w]\n" " tcplay --modify -d device [-k keyfile] --restore-from-backup-hdr [-w]\n" " tcplay -j mapping\n" " tcplay -u mapping\n" " tcplay -h | -v\n" "\n" "Valid commands are:\n" " -c, --create\n" "\t Creates a new TC volume on the device specified by -d or --device.\n" " -h, --help\n" "\t Print help message and exit.\n" " -i, --info\n" "\t Gives information about the TC volume specified by -d or --device.\n" " -j , --info-mapped=\n" "\t Gives information about the mapped TC volume under the given mapping.\n" " -m , --map=\n" "\t Creates a dm-crypt mapping with the given name for the device\n" "\t specified by -d or --device.\n" " -u , --unmap=\n" "\t Removes a dm-crypt mapping with the given name.\n" " --modify\n" "\t Changes the volume's passphrase, keyfile and optionally the hashing\n" "\t function used for the PBKDF password derivation.\n" " -v, --version\n" "\t Print version message and exit.\n" "\n" "Valid options for --create are:\n" " -a , --pbkdf-prf=\n" "\t Specifies which hashing function to use for the PBKDF password\n" "\t derivation when creating a new volume.\n" "\t To see valid options, specify '-a help'.\n" " -b , --cipher=\n" "\t Specifies which cipher to use when creating a new TC volume.\n" "\t To see valid options, specify '-b help'.\n" " -g, --hidden\n" "\t Specifies that the newly created volume will contain a hidden volume.\n" " -x , --pbkdf-prf=\n" "\t Specifies which hashing function to use for the PBKDF password\n" "\t derivation when creating a new hidden volume. By default, the\n" "\t same as for the outer volume will be used.\n" "\t To see valid options, specify '-x help'.\n" " -y , --cipher=\n" "\t Specifies which cipher to use when creating a new hidden volume.\n" "\t By default, the same as for the outer volume will be used.\n" "\t To see valid options, specify '-y help'.\n" " -z, --insecure-erase\n" "\t Skips the erase of the disk. Possible security hazard.\n" " -w, --weak-keys\n" "\t Uses a weak source of entropy (urandom) for key material.\n" "\t WARNING: This is a REALLY REALLY bad idea for anything but\n" "\t testing.\n" "\n" "Valid options for --modify are:\n" " --new-keyfile=\n" "\t Specifies a key file to use for the password derivation, when\n" "\t re-encrypting the header, can appear multiple times.\n" " --new-pbkdf-prf=\n" "\t Specifies which hashing function to use for the PBKDF password\n" "\t derivation when re-encrypting the header.\n" "\t To see valid options, specify '-a help'.\n" " -s , --system-encryption=\n" "\t Specifies that the disk (e.g. /dev/da0) is using system encryption.\n" " --fde\n" "\t Specifies that the disk (e.g. /dev/da0) is using full disk encryption.\n" " --use-backup\n" "\t Uses the backup headers (at the end of the volume) instead of the\n" "\t primary headers. Both normal and backup headers will be modified!\n" "\t This is useful when your primary headers have been corrupted.\n" " --restore-from-backup-hdr\n" "\t Implies --use-backup, no new PBKDF hashing function, no new keyfiles\n" "\t and no new passphrase.\n" "\t In other words, this will simply restore both headers from the backup\n" "\t header." " -w, --weak-keys\n" "\t Uses a weak source of entropy (urandom) for salt material. The\n" "\t key material is not affected, as the master keys are kept intact.\n" "\t WARNING: This is a bad idea for anything but testing.\n" "\n" "Valid options for --info and --map are:\n" " -e, --protect-hidden\n" "\t Protect a hidden volume when mounting the outer volume.\n" " -s , --system-encryption=\n" "\t Specifies that the disk (e.g. /dev/da0) is using system encryption.\n" " --fde\n" "\t Specifies that the disk (e.g. /dev/da0) is using full disk encryption.\n" " --use-backup\n" "\t Uses the backup headers (at the end of the volume) instead of the\n" "\t primary headers.\n" "\t This is useful when your primary headers have been corrupted.\n" "\n" "Valid options common to all commands are:\n" " -d , --device=\n" "\t Specifies the path to the volume to operate on (e.g. /dev/da0s1).\n" " -f , --keyfile-hidden=\n" "\t Specifies a key file to use for the hidden volume password derivation.\n" "\t This option is only valid in combination with -e, --protect-hidden\n" "\t or -g, --hidden.\n" " -k , --keyfile=\n" "\t Specifies a key file to use for the password derivation, can appear\n" "\t multiple times.\n" ); exit(EXIT_FAILURE); } static struct option longopts[] = { { "create", no_argument, NULL, 'c' }, { "cipher", required_argument, NULL, 'b' }, { "cipher-hidden", required_argument, NULL, 'y' }, { "hidden", no_argument, NULL, 'g' }, { "pbkdf-prf", required_argument, NULL, 'a' }, { "pbkdf-prf-hidden", required_argument, NULL, 'x' }, { "info", no_argument, NULL, 'i' }, { "info-mapped", required_argument, NULL, 'j' }, { "map", required_argument, NULL, 'm' }, { "keyfile", required_argument, NULL, 'k' }, { "keyfile-hidden", required_argument, NULL, 'f' }, { "protect-hidden", no_argument, NULL, 'e' }, { "device", required_argument, NULL, 'd' }, { "system-encryption", required_argument, NULL, 's' }, { "fde", no_argument, NULL, FLAG_LONG_FDE }, { "use-backup", no_argument, NULL, FLAG_LONG_USE_BACKUP }, { "modify", no_argument, NULL, FLAG_LONG_MOD }, { "new-keyfile", required_argument, NULL, FLAG_LONG_MOD_KF }, { "new-pbkdf-prf", required_argument, NULL, FLAG_LONG_MOD_PRF }, { "restore-from-backup-hdr", no_argument, NULL, FLAG_LONG_MOD_NONE }, { "unmap", required_argument, NULL, 'u' }, { "version", no_argument, NULL, 'v' }, { "weak-keys", no_argument, NULL, 'w' }, { "insecure-erase", no_argument, NULL, 'z' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, 0 }, }; int main(int argc, char *argv[]) { const char *dev = NULL, *sys_dev = NULL, *map_name = NULL; const char *keyfiles[MAX_KEYFILES]; const char *h_keyfiles[MAX_KEYFILES]; const char *new_keyfiles[MAX_KEYFILES]; int nkeyfiles; int n_hkeyfiles; int n_newkeyfiles; int ch, error; int flags = 0; int info_vol = 0, map_vol = 0, protect_hidden = 0, unmap_vol = 0, info_map = 0, create_vol = 0, modify_vol = 0, contain_hidden = 0, use_secure_erase = 1, use_weak_keys = 0; struct pbkdf_prf_algo *prf = NULL; struct tc_cipher_chain *cipher_chain = NULL; struct pbkdf_prf_algo *h_prf = NULL; struct tc_cipher_chain *h_cipher_chain = NULL; struct pbkdf_prf_algo *new_prf = NULL; if ((error = tc_play_init()) != 0) { fprintf(stderr, "Initialization failed, exiting."); exit(EXIT_FAILURE); } atexit(check_and_purge_safe_mem); signal(SIGUSR1, sig_handler); signal(SIGINFO, sig_handler); nkeyfiles = 0; n_hkeyfiles = 0; n_newkeyfiles = 0; while ((ch = getopt_long(argc, argv, "a:b:cd:ef:ghij:k:m:s:u:vwx:y:z", longopts, NULL)) != -1) { switch(ch) { case 'a': if (prf != NULL) usage(); if ((prf = check_prf_algo(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'b': if (cipher_chain != NULL) usage(); if ((cipher_chain = check_cipher_chain(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'c': create_vol = 1; break; case 'd': dev = optarg; break; case 'e': protect_hidden = 1; break; case 'f': h_keyfiles[n_hkeyfiles++] = optarg; break; case 'g': contain_hidden = 1; break; case 'i': info_vol = 1; break; case 'j': info_map = 1; map_name = optarg; break; case 'k': keyfiles[nkeyfiles++] = optarg; break; case 'm': map_vol = 1; map_name = optarg; break; case 's': flags |= TC_FLAG_SYS; sys_dev = optarg; break; case 'u': unmap_vol = 1; map_name = optarg; break; case 'v': printf("tcplay v%d.%d\n", MAJ_VER, MIN_VER); exit(EXIT_SUCCESS); /* NOT REACHED */ case 'w': fprintf(stderr, "WARNING: Using urandom as source of " "entropy for key material is a really bad idea.\n"); use_weak_keys = 1; break; case 'x': if (h_prf != NULL) usage(); if ((h_prf = check_prf_algo(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'y': if (h_cipher_chain != NULL) usage(); if ((h_cipher_chain = check_cipher_chain(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case 'z': use_secure_erase = 0; break; case FLAG_LONG_FDE: flags |= TC_FLAG_FDE; break; case FLAG_LONG_USE_BACKUP: flags |= TC_FLAG_BACKUP; break; case FLAG_LONG_MOD: modify_vol = 1; break; case FLAG_LONG_MOD_KF: new_keyfiles[n_newkeyfiles++] = optarg; break; case FLAG_LONG_MOD_PRF: if (new_prf != NULL) usage(); if ((new_prf = check_prf_algo(optarg, 0)) == NULL) { if (strcmp(optarg, "help") == 0) exit(EXIT_SUCCESS); else usage(); /* NOT REACHED */ } break; case FLAG_LONG_MOD_NONE: new_prf = NULL; n_newkeyfiles = 0; flags |= TC_FLAG_ONLY_RESTORE; flags |= TC_FLAG_BACKUP; break; case 'h': case '?': default: usage(); /* NOT REACHED */ } } argc -= optind; argv += optind; /* Check arguments */ if (!(((map_vol || info_vol || create_vol || modify_vol) && dev != NULL) || ((unmap_vol || info_map) && map_name != NULL)) || (TC_FLAG_SET(flags, SYS) && TC_FLAG_SET(flags, FDE)) || (map_vol + info_vol + create_vol + unmap_vol + info_map + modify_vol > 1) || (contain_hidden && !create_vol) || (TC_FLAG_SET(flags, SYS) && (sys_dev == NULL)) || (TC_FLAG_SET(flags, ONLY_RESTORE) && (n_newkeyfiles > 0 || new_prf != NULL)) || (TC_FLAG_SET(flags, BACKUP) && (sys_dev != NULL || TC_FLAG_SET(flags, FDE))) || (map_vol && (map_name == NULL)) || (unmap_vol && (map_name == NULL)) || (!modify_vol && n_newkeyfiles > 0) || (!modify_vol && new_prf != NULL) || (!modify_vol && TC_FLAG_SET(flags, ONLY_RESTORE)) || (!(protect_hidden || create_vol) && n_hkeyfiles > 0)) { usage(); /* NOT REACHED */ } /* Create a new volume */ if (create_vol) { error = create_volume(dev, contain_hidden, keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, prf, cipher_chain, h_prf, h_cipher_chain, NULL, NULL, 0, 1 /* interactive */, use_secure_erase, use_weak_keys); if (error) { tc_log(1, "could not create new volume on %s\n", dev); } } else if (info_map) { error = info_mapped_volume(map_name, 1 /* interactive */); } else if (info_vol) { error = info_volume(dev, flags, sys_dev, protect_hidden, keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL, 1 /* interactive */, DEFAULT_RETRIES, 0); } else if (map_vol) { error = map_volume(map_name, dev, flags, sys_dev, protect_hidden, keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL, 1 /* interactive */, DEFAULT_RETRIES, 0); } else if (unmap_vol) { error = dm_teardown(map_name, NULL); } else if (modify_vol) { error = modify_volume(dev, flags, sys_dev, keyfiles, nkeyfiles, new_keyfiles, n_newkeyfiles, new_prf, NULL, NULL, 1 /* interactive */, DEFAULT_RETRIES, 0, use_weak_keys); } return error; } tc-play-1.1/pbkdf2-gcrypt.c000066400000000000000000000046731217612412400155600ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include /* * Yey for gcrypt and its broken includes... * see http://lists.gnupg.org/pipermail/gcrypt-devel/2011-July/001830.html * and http://seclists.org/wireshark/2011/Jul/208 * for more details... */ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #pragma GCC diagnostic warning "-Wdeprecated-declarations" #include "tcplay.h" static int get_gcrypt_hash_id(struct pbkdf_prf_algo *hash) { if (strcmp(hash->name, "RIPEMD160") == 0) return GCRY_MD_RMD160; else if (strcmp(hash->name, "SHA512") == 0) return GCRY_MD_SHA512; else if (strcmp(hash->name, "whirlpool") == 0) return GCRY_MD_WHIRLPOOL; else return -1; } int pbkdf2(struct pbkdf_prf_algo *hash, const char *pass, int passlen, const unsigned char *salt, int saltlen, int keylen, unsigned char *out) { gpg_error_t err; err = gcry_kdf_derive(pass, passlen, GCRY_KDF_PBKDF2, get_gcrypt_hash_id(hash), salt, saltlen, hash->iteration_count, keylen, out); if (err) { tc_log(1, "Error in PBKDF2\n"); return EINVAL; } return 0; } tc-play-1.1/pbkdf2-openssl.c000066400000000000000000000037221217612412400157250ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include "tcplay.h" int pbkdf2(struct pbkdf_prf_algo *hash, const char *pass, int passlen, const unsigned char *salt, int saltlen, int keylen, unsigned char *out) { const EVP_MD *md; int r; OpenSSL_add_all_algorithms(); md = EVP_get_digestbyname(hash->name); if (md == NULL) { tc_log(1, "Hash %s not found\n", hash->name); return ENOENT; } r = PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, hash->iteration_count, md, keylen, out); if (r == 0) { tc_log(1, "Error in PBKDF2\n"); return EINVAL; } return 0; } tc-play-1.1/safe_mem.c000066400000000000000000000107441217612412400146520ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include "tcplay.h" struct safe_mem_hdr { struct safe_mem_hdr *prev; struct safe_mem_hdr *next; struct safe_mem_tail *tail; const char *file; int line; size_t alloc_sz; char sig[8]; /* SAFEMEM */ }; struct safe_mem_tail { char sig[8]; /* SAFEMEM */ }; static struct safe_mem_hdr *safe_mem_hdr_first = NULL; void * _alloc_safe_mem(size_t req_sz, const char *file, int line) { struct safe_mem_hdr *hdr, *hdrp; struct safe_mem_tail *tail; size_t alloc_sz; char *mem, *user_mem; alloc_sz = req_sz + sizeof(*hdr) + sizeof(*tail); if ((mem = malloc(alloc_sz)) == NULL) return NULL; if (mlock(mem, alloc_sz) < 0) { free(mem); return NULL; } memset(mem, 0, alloc_sz); hdr = (struct safe_mem_hdr *)mem; tail = (struct safe_mem_tail *)(mem + alloc_sz - sizeof(*tail)); user_mem = mem + sizeof(*hdr); strcpy(hdr->sig, "SAFEMEM"); strcpy(tail->sig, "SAFEMEM"); hdr->tail = tail; hdr->alloc_sz = alloc_sz; hdr->file = file; hdr->line = line; hdr->next = NULL; if (safe_mem_hdr_first == NULL) { safe_mem_hdr_first = hdr; } else { hdrp = safe_mem_hdr_first; while (hdrp->next != NULL) hdrp = hdrp->next; hdr->prev = hdrp; hdrp->next = hdr; } return user_mem; } void _free_safe_mem(void *mem_ptr, const char *file, int line) { struct safe_mem_hdr *hdr; struct safe_mem_tail *tail; size_t alloc_sz; char *mem = mem_ptr; mem -= sizeof(*hdr); hdr = (struct safe_mem_hdr *)mem; tail = (struct safe_mem_tail *)(mem + hdr->alloc_sz - sizeof(*tail)); #ifdef DEBUG fprintf(stderr, "freeing safe_mem (hdr): %#lx (%s:%d)\n", (unsigned long)(void *)hdr, hdr->file, hdr->line); #endif if (hdr->alloc_sz == 0) { fprintf(stderr, "BUG: double-free at %s:%d !!!\n", file, line); return; } /* Integrity checks */ if ((memcmp(hdr->sig, "SAFEMEM\0", 8) != 0) || (memcmp(tail->sig, "SAFEMEM\0", 8) != 0)) { fprintf(stderr, "BUG: safe_mem buffer under- or overflow at " "%s:%d !!!\n", file, line); return; } if (safe_mem_hdr_first == NULL) { fprintf(stderr, "BUG: safe_mem list should not be empty at " "%s:%d !!!\n", file, line); return; } if (hdr->prev != NULL) hdr->prev->next = hdr->next; if (hdr->next != NULL) hdr->next->prev = hdr->prev; if (safe_mem_hdr_first == hdr) safe_mem_hdr_first = hdr->next; alloc_sz = hdr->alloc_sz; memset(mem, 0xFF, alloc_sz); memset(mem, 0, alloc_sz); free(mem); } void check_and_purge_safe_mem(void) { struct safe_mem_hdr *hdr; char *mem; #ifdef DEBUG int ok; #endif if (safe_mem_hdr_first == NULL) return; hdr = safe_mem_hdr_first; while ((hdr = safe_mem_hdr_first) != NULL) { #ifdef DEBUG if ((hdr->alloc_sz > 0) && (memcmp(hdr->sig, "SAFEMEM\0", 8) == 0) && (memcmp(hdr->tail->sig, "SAFEMEM\0", 8) == 0)) ok = 1; else ok = 0; fprintf(stderr, "un-freed safe_mem: %#lx (%s:%d) [integrity=%s]\n", (unsigned long)(void *)hdr, hdr->file, hdr->line, ok? "ok" : "failed"); #endif mem = (void *)hdr; mem += sizeof(*hdr); _free_safe_mem(mem, "check_and_purge_safe_mem", 0); } } tc-play-1.1/tcplay.3000066400000000000000000000336031217612412400143110ustar00rootroot00000000000000.\" .\" Copyright (c) 2011 The DragonFly Project. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" 3. Neither the name of The DragonFly Project nor the names of its .\" contributors may be used to endorse or promote products derived .\" from this software without specific, prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS .\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE .\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, .\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; .\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED .\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, .\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd July 30, 2013 .Dt TCPLAY 3 .Os .Sh NAME .Nm tc_api_init , .Nm tc_api_uninit , .Nm tc_api_info_volume , .Nm tc_api_info_mapped_volume , .Nm tc_api_create_volume , .Nm tc_api_modify_volume , .Nm tc_api_map_volume , .Nm tc_api_unmap_volume , .Nm tc_api_check_cipher , .Nm tc_api_check_prf_hash , .Nm tc_api_get_error_msg , .Nm tc_api_get_summary , .Nm tc_api_get_state .Nd TrueCrypt volume management .Sh LIBRARY .Lb libtcplay .Sh SYNOPSIS .In tcplay_api.h .Ft int .Fn tc_api_init "int verbose" .Ft int .Fn tc_api_uninit "void" .Ft int .Fn tc_api_info_volume "tc_api_opts *api_opts" "tc_api_volinfo *info" .Ft int .Fn tc_api_info_mapped_volume "tc_api_opts *api_opts" "tc_api_volinfo *info" .Ft int .Fn tc_api_create_volume "tc_api_opts *api_opts" .Ft int .Fn tc_api_modify_volume "tc_api_opts *api_opts" .Ft int .Fn tc_api_map_volume "tc_api_opts *api_opts" .Ft int .Fn tc_api_unmap_volume "tc_api_opts *api_opts" .Ft int .Fn tc_api_check_cipher "tc_api_opts *api_opts" .Ft int .Fn tc_api_check_prf_hash "tc_api_opts *api_opts" .Ft const char * .Fn tc_api_get_error_msg "void" .Ft const char * .Fn tc_api_get_summary "void" .Ft tc_api_state .Fn tc_api_get_state "float *progress_pct" .Sh DESCRIPTION The .Nm tcplay library provides an interface to create, query and map TrueCrypt-compatible volumes. .Pp The .Fn tc_api_create_volume , .Fn tc_api_modify_volume , .Fn tc_api_map_volume , .Fn tc_api_unmap_volume , .Fn tc_api_check_cipher and .Fn tc_api_check_prf_hash functions take a .Vt tc_api_opts data structure as only argument, which is defined as follows: .Bd -literal typedef struct tc_api_opts { /* Common fields */ const char *tc_device; const char *tc_passphrase; const char **tc_keyfiles; const char **tc_passphrase_hidden; const char **tc_keyfiles_hidden; /* Fields for mapping / info */ const char *tc_map_name; int tc_protect_hidden; /* Fields for mapping / info / modify */ int tc_password_retries; int tc_interactive_prompt; unsigned long tc_prompt_timeout; int tc_use_system_encryption; const char *tc_system_device; int tc_use_fde; int tc_use_backup; /* Fields for modify */ const char *tc_new_passphrase; const char **tc_new_keyfiles; const char *tc_new_prf_hash; int tc_use_weak_salt; /* Fields for creation */ const char *tc_cipher; const char *tc_prf_hash; const char *tc_cipher_hidden; const char *tc_prf_hash_hidden; size_t tc_size_hidden_in_bytes; int tc_no_secure_erase; int tc_use_weak_keys; } tc_api_opts; .Ed .Pp where the keyfile fields, .Fa tc_keyfiles , .Fa tc_keyfiles_hidden and .Fa tc_new_keyfiles are .Dv NULL terminated arrays of key file strings. .Pp The .Fn tc_api_info_volume and .Fn tc_api_info_mapped_volume functions take a .Vt tc_api_volinfo data structure as second argument, which is defined as follows: .Bd -literal typedef struct tc_api_volinfo { char tc_device[1024]; char tc_cipher[256]; char tc_prf[64]; int tc_key_bits; size_t tc_size; /* in bytes */ off_t tc_iv_offset; /* in bytes */ off_t tc_block_offset; /* in bytes */ } tc_api_volinfo; .Ed .Pp The .Fn tc_api_init function initializes the library internals and prepares it for use via the API. This function has to be called before using any other API function. If the .Fa verbose argument is non-zero, then the library will output information such as errors via stdout and stderr. .Pp The .Fn tc_api_uninit function clears up all internal secure memory, such as memory used for decrypted headers, passphrases, keyfiles, etc. .Pp The .Fn tc_api_info_volume function retrieves information about a volume using the parameters specified in the .Fa api_opts argument. All fields except .Fa tc_map_name are used in the same way as for .Fn tc_api_map_volume . The retrieved information is placed into the .Fa info argument. The .Fa tc_cipher and .Fa tc_prf fields will contain a string describing the block cipher (chain) and PBKDF2 PRF algorithm respectively. The .Fa tc_key_bits field gives the total key size used for the block cipher(s). The .Fa tc_size field gives the size of the volume in bytes. The .Fa tc_iv_offset and .Fa tc_block_offset fields give the IV and block offset in bytes of the volume, respectively. The .Fa tc_device field contains the path to the raw encrypted block device. .Pp The .Fn tc_api_info_mapped_volume is similar to the .Fn tc_api_info_volume function, but is to be used on already mapped volumes and doesn't require any passphrase or keyfiles. The .Fa tc_map_name field in the .Fa api_opts parameter determines which mapped volume is to be queried. The retrieved information is placed into the .Fa info argument. All fields will be populated as when calling .Fn tc_api_info_volume except for the .Fa tc_prf field, which will contain the string "(unknown)". .Pp The .Fn tc_api_create_volume function creates a new volume using the parameters specified in the .Fa api_opts argument. The new volume will be created on the device specified by .Fa tc_device using the cipher specified by .Fa tc_cipher and the pbkdf2 prf hash algorithm specified by .Fa tc_prf_hash and using the passphrase and keyfiles specified by .Fa tc_passphrase and .Fa tc_keyfiles respectively. If .Fa tc_size_hidden_in_bytes is not zero, a hidden volume of the given size will be created, using the cipher specified by .Fa tc_cipher_hidden and the pbkdf2 prf hash algorithm specified by .Fa tc_prf_hash_hidden . If .Fa tc_cipher_hidden or .Fa tc_prf_hash_hidden are .Dv NULL , the same algorithm as for the outer volume will be used. The passphrase and keyfiles used are specified by .Fa tc_passphrase_hidden and .Fa tc_keyfiles_hidden respectively. If .Fa tc_no_secure_erase is specified, no erase will be performed. If .Fa tc_use_weak_keys is specified, the key material for the master key will be taken from .Pa /dev/urandom instead of .Pa /dev/random . This option should never be used other than for testing. .Pp The .Fn tc_api_modify_volume function modifies a volume (header) according to the parameters specified in the .Fa api_opts parameter. The volume is specified in .Fa tc_device . The .Fa tc_interactive_prompt field determines whether the user will be prompted to enter a passphrase interactively or whether the passphrase in .Fa tc_passphrase will be used. If an interactive prompt is used, the prompt will time out after .Fa tc_prompt_timeout seconds. A value of 0 indicates that no timeout will occur. The number of passphrase entry retries is defined by .Fa tc_password_retries . Depending on the passphrase/keyfiles used either the outer or the hidden volume header will be modified. Any keyfiles that are needed to unlock the volume are specified in .Fa tc_keyfiles . If .Fa tc_use_system_encryption is specified, a device using system encryption can be accessed. The .Fa tc_system_device should point to the parent device (i.e. underlying physical disk), while the .Fa tc_device argument should point to the actual encrypted partition. If .Fa tc_use_fde is specified, the device pointed to by .Fa tc_device should be a whole disk device, not any partition. If .Fa tc_use_backup is specified, .Nm tcplay will use the backup headers at the end of a volume instead of the primary headers as template for the modification. Both the backup and the main header will always be written as part of a .Fn tc_api_modify_volume call. The .Fa tc_new_passphrase and .Fa tc_new_keyfiles arguments specify the new passphrase and keyfile(s) to be used, respectively. The .Fa tc_new_passphrase argument will only be used if .Fa tc_interactive_prompt is not set, otherwise the user will be prompted for the new passphrase. The .Fa tc_new_prf_hash specifies the PBKDF2 PRF hash algorithm to be used when reencrypting the header. If it is .Dv NULL , the same PBKDF2 PRF hash function will be used that the header already uses. If .Fa tc_use_weak_salt is set, a weak source of entropy will be used for the salt of both the main and backup headers. This option does not affect the entropy of the master volume keys, as these are not modified. .Pp The .Fn tc_api_map_volume function maps a volume using the parameters specified in the .Fa api_opts argument. The volume, which will be mapped as .Fa tc_map_name , is specified in .Fa tc_device . The .Fa tc_interactive_prompt field determines whether the user will be prompted to enter a passphrase interactively or whether the passphrase in .Fa tc_passphrase will be used. If an interactive prompt is used, the prompt will time out after .Fa tc_prompt_timeout seconds. A value of 0 indicates that no timeout will occur. The number of passphrase entry retries is defined by .Fa tc_password_retries . Depending on the passphrase/keyfiles used either the outer or the hidden volume will be mapped. If .Fa tc_protect_hidden is specified, the outer volume will be mapped, but its size will be adjusted so that it does not map over the hidden volume - the hidden volume will hence be protected from any accidental overwriting. If .Fa tc_protect_hidden is specified, the passphrase and keyfiles for the hidden volume must be specified in .Fa tc_passphrase_hidden and .Fa tc_keyfiles_hidden . If .Fa tc_use_system_encryption is specified, a device using system encryption can be accessed. The .Fa tc_system_device should point to the parent device (i.e. underlying physical disk), while the .Fa tc_device argument should point to the actual encrypted partition. If .Fa tc_use_fde is specified, the device pointed to by .Fa tc_device should be a whole disk device, not any partition. The device will be mapped or queried as a whole. To access individual partitions, a utility such as .Xr kpartx 8 should be used, which will create additional individual mappings for each partition in the decrypted mapped volume. For more details on full disk encryption, see .Xr tcplay 8 . If .Fa tc_use_backup is specified, .Nm tcplay will use the backup headers at the end of a volume instead of the primary headers to access it. .Pp The .Fn tc_api_unmap_volume unmaps / closes the volume specified in .Fa tc_map_name . .Pp The .Fn tc_api_check_cipher function checks whether the cipher specified in the .Fa api_opts argument field .Fa tc_cipher is valid. .Pp The .Fn tc_api_check_prf_hash function checks whether the prf hash algorithm specified in the .Fa api_opts argument field .Fa tc_prf_hash is valid. .Pp The .Fn tc_api_get_error_msg function should be called whenver another API function returns .Dv TC_ERR . It returns a string containing a description of the error that occured. .Pp The .Fn tc_api_get_summary function returns a string containing a summary of the current progress of a certain operation. Currently only the volume erasing part of creating a new volume can provide a summary. When no summary is available, an empty string is returned. The output otherwise is equivalent to that of a .Dv SIGINFO signal when using .Xr tcplay 8 . .Pp The .Fn tc_api_get_state function returns information on the current state of .Nm tcplay . Three states are currently reported: .Bl -tag -width indent .It Dv TC_STATE_GET_RANDOM is reported when .Nm tcplay is gathering entropy for key material. .It Dv TC_STATE_ERASE is reported when the volume is being erased. .It Dv TC_STATE_UNKNOWN is reported in all other cases. .El .Pp The two states .Dv TC_STATE_GET_RANDOM and .Dv TC_STATE_ERASE are reported separately, as they are the only lengthy steps during the use of tcplay. The .Fa progress_pct parameter, if not .Dv NULL , will be set to the percentage completed so far of these operations. .Sh NOTES TrueCrypt limits passphrases to 64 characters (including the terminating null character). To be compatible with it, .Nm tcplay does the same. All passphrases (exlcuding keyfiles) are trimmed to 64 characters. Similarly, keyfiles are limited to a size of 1 MB, but up to 256 keyfiles can be used. .Sh RETURN VALUES All functions except .Fn tc_api_get_error_msg and .Fn tc_api_get_summary return either .Dv TC_OK to signal that the operation completed successfully, or .Dv TC_ERR to signal that an error occured. .Pp The .Fn tc_api_get_error_msg and .Fn tc_api_get_summary functions always return a valid, but possibly empty, string. .Pp The .Fn tc_api_get_state function always returns one of: .Dv TC_STATE_UNKNOWN , .Dv TC_STATE_ERASE or .Dv TC_STATE_GET_RANDOM . .Sh COMPATIBILITY The .Nm tcplay library offers full compatibility with TrueCrypt volumes including hidden volumes, system encryption (map-only), keyfiles and cipher cascading. .Sh SEE ALSO .Xr tcplay 8 , .Xr kpartx 8 .Sh HISTORY The .Nm tcplay library appeared in .Dx 2.11 . .Sh AUTHORS .An Alex Hornung tc-play-1.1/tcplay.8000066400000000000000000000327241217612412400143210ustar00rootroot00000000000000.\" .\" Copyright (c) 2011 .\" The DragonFly Project. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in .\" the documentation and/or other materials provided with the .\" distribution. .\" 3. Neither the name of The DragonFly Project nor the names of its .\" contributors may be used to endorse or promote products derived .\" from this software without specific, prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT .\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS .\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE .\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, .\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; .\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED .\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, .\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd July 29, 2013 .Dt TCPLAY 8 .Os .Sh NAME .Nm tcplay .Nd tool to manage TrueCrypt volumes .Sh SYNOPSIS .Nm .Fl c .Fl d Ar device .Op Fl g .Op Fl z .Op Fl w .Op Fl a Ar pbkdf_hash .Op Fl b Ar cipher .Op Fl f Ar keyfile_hidden .Op Fl k Ar keyfile .Op Fl x Ar pbkdf_hash .Op Fl y Ar cipher .Nm .Fl i .Fl d Ar device .Op Fl e .Op Fl f Ar keyfile_hidden .Op Fl k Ar keyfile .Op Fl s Ar system_device .Op Fl -fde .Op Fl -use-backup .Nm .Fl j Ar mapping .Nm .Fl m Ar mapping .Fl d Ar device .Op Fl e .Op Fl f Ar keyfile_hidden .Op Fl k Ar keyfile .Op Fl s Ar system_device .Op Fl -fde .Op Fl -use-backup .Nm .Fl -modify .Fl d Ar device .Op Fl k Ar keyfile .Op Fl -new-keyfile Ar new_keyfile .Op Fl -new-pbkdf-prf Ar pbkdf_hash .Op Fl s Ar system_device .Op Fl -fde .Op Fl -use-backup .Op Fl w .Nm .Fl -modify .Fl d Ar device .Op Fl k Ar keyfile .Fl -restore-from-backup-hdr .Op Fl w .Nm .Fl u Ar mapping .Nm .Fl h | v .Sh DESCRIPTION The .Nm utility provides full support for creating and opening/mapping TrueCrypt-compatible volumes. It supports the following commands, each with a set of options detailed further below: .Bl -tag -width indent .It Fl c , Fl -create Create a new encrypted TrueCrypt volume on the device specified by .Fl -device . .It Fl h, Fl -help Print help message and exit. .It Fl i , Fl -info Print out information about the encrypted device specified by .Fl -device . .It Fl j Ar mapping , Fl -info-mapped Ns = Ns Ar mapping Print out information about the mapped tcplay volume specified by .Ar mapping . Information such as key CRC and the PBKDF2 PRF is not available via this command. .It Fl -modify Modify the volume header. This mode allows changing passphrase, keyfiles, PBKDF2 PRF as well as restoring from a backup header. .It Fl m Ar mapping , Fl -map Ns = Ns Ar mapping Map the encrypted TrueCrypt volume on the device specified by .Fl -device as a .Xr dm 4 mapping called .Ar mapping . The .Ar mapping argument should not contain any spaces or special characters. .It Fl u Ar mapping , Fl -unmap Ns = Ns Ar mapping Removes (unmaps) the .Xr dm 4 mapping specified by .Ar mapping as well as any related cascade mappings. If you mapped a volume using full disk encryption and created mapping for individual partitions using .Xr kpartx 8 , you must remove these prior to unmapping the volume. .It Fl v, Fl -version Print version message and exit. .El .Pp Options common to all commands are: .Bl -tag -width indent .It Fl d Ar device , Fl -device Ns = Ns Ar device Specifies the disk .Ar device on which the TrueCrypt volume resides/will reside. This option is mandatory for all commands. .It Fl f Ar keyfile_hidden , Fl -keyfile-hidden Ns = Ns Ar keyfile_hidden Specifies a keyfile to use in addition to the passphrase when either creating a hidden volume or when protecting a hidden volume while mapping or querying the outer volume. If you only intend to map a hidden volume, the .Fl -keyfile option has to be used. This option can appear multiple times; if so, multiple keyfiles will be used. This option is not valid in the .Fl -modify mode. .It Fl k Ar keyfile , Fl -keyfile Ns = Ns Ar keyfile Specifies a .Ar keyfile to use in addition to the passphrase. This option can appear multiple times; if so, multiple keyfiles will be used. .El .Pp Additional options for the .Fl -create command are: .Bl -tag -width indent .It Fl a Ar pbkdf_hash , Fl -pbkdf-prf Ns = Ns Ar pbkdf_hash Specifies which hash algorithm to use for the PBKDF2 password derivation. To see which algorithms are supported, specify .Fl -pbkdf-prf Ns = Ns Cm help . .It Fl b Ar cipher , Fl -cipher Ns = Ns Ar cipher Specifies which cipher algorithm or cascade of ciphers to use to encrypt the new volume. To see which algorithms are supported, specify .Fl -cipher Ns = Ns Cm help . .It Fl g, Fl -hidden Specifies that the newly created volume will contain a hidden volume. The keyfiles applied to the passphrase for the hidden volume are those specified by .Fl -keyfile-hidden . The user will be prompted for the size of the hidden volume interactively. .It Fl w, Fl -weak-keys Use .Xr urandom 4 for key material instead of a strong entropy source. This is in general a really bad idea and should only be used for testing. .It Fl x Ar pbkdf_hash , Fl -pbkdf-prf-hidden Ns = Ns Ar pbkdf_hash Specifies which hash algorithm to use for the PBKDF2 password derivation for the hidden volume. Only valid in conjunction with .Fl -hidden . If no algorithm is specified, the same as for the outer volume will be used. To see which algorithms are supported, specify .Fl -pbkdf-prf-hidden Ns = Ns Cm help . .It Fl y Ar cipher , Fl -cipher-hidden Ns = Ns Ar cipher Specifies which cipher algorithm or cascade of ciphers to use to encrypt the hidden volume on the new TrueCrypt volume. Only valid in conjunction with .Fl -hidden . If no cipher is specified, the same as for the outer volume will be used. To see which algorithms are supported, specify .Fl -cipher-hidden Ns = Ns Cm help . .It Fl z, Fl -insecure-erase Skips the secure erase of the disk. Use this option carefully as it is a security risk! .El .Pp Additional options for the .Fl -info , .Fl -map and .Fl -modify commands are: .Bl -tag -width indent .It Fl e, Fl -protect-hidden Specifies that an outer volume will be queried or mapped, but its reported size will be adjusted accordingly to the size of the hidden volume contained in it. Both the hidden volume and outer volume passphrase and keyfiles will be required. This option only applies to the .Fl -info and .Fl -map commands. .It Fl s Ar system_device , Fl -system-encryption Ns = Ns Ar system_device This option is required if you are attempting to access a device that uses system encryption, for example an encrypted .Tn Windows system partition. It does not apply to disks using full disk encryption. The .Fl -device option will point at the actual encrypted partition, while the .Ar system_device argument will point to the parent device (i.e.\& underlying physical disk) of the encrypted partition. .It Fl -fde This option is intended to be used with disks using full disk encryption (FDE). When a disk has been encrypted using TrueCrypt's FDE, the complete disk is encrypted except for the first 63 sectors. The .Fl -device option should point to the whole disk device, not to any particular partition. The resultant mapping will cover the whole disk, and will not appear as separate partitions. To access individual partitions after mapping, .Xr kpartx 8 can be used. .It Fl -use-backup This option is intended to be used when the primary headers of a volume have been corrupted. This option will force .Nm to use the backup headers, which are located at the end of the device, to access the volume. .El .Pp Additional options only for the .Fl -modify command are: .Bl -tag -width indent .It Fl -new-pbkdf-prf Ns = Ns Ar pbkdf_hash Specifies which hash algorithm to use for the PBKDF2 password derivation on reencrypting the volume header. If this option is not specified, the reencrypted header will use the current PRF. To see which algorithms are supported, specify .Fl -pbkdf-prf Ns = Ns Cm help . .It Fl -new-keyfile Ns = Ns Ar keyfile Specifies a .Ar keyfile to use in addition to the new passphrase on reencrypting the volume header. This option can appear multiple times; if so, multiple keyfiles will be used. .It Fl -restore-from-backup-hdr If this option is specified, neither .Fl -new-pbkdf-prf nor .Fl -new-keyfile should be specified. This option implies .Fl -use-backup . Use this option to restore the volume headers from the backup header. .El .Sh NOTES TrueCrypt limits passphrases to 64 characters (including the terminating null character). To be compatible with it, .Nm does the same. All passphrases (excluding keyfiles) are trimmed to 64 characters. Similarly, keyfiles are limited to a size of 1 MB, but up to 256 keyfiles can be used. .Sh PLAUSIBLE DENIABILITY .Nm offers plausible deniability. Hidden volumes are created within an outer volume. Which volume is accessed solely depends on the passphrase and keyfile(s) used. If the passphrase and keyfiles for the outer volume are specified, no information about the existance of the hidden volume is exposed. Without knowledge of the passphrase and keyfile(s) of the hidden volume its existence remains unexposed. The hidden volume can be protected when mapping the outer volume by using the .Fl -protect-hidden option and specifying the passphrase and keyfiles for both the outer and hidden volumes. .Sh EXAMPLES Create a new TrueCrypt volume on .Pa /dev/vn0 using the cipher cascade of AES and Twofish and the Whirlpool hash algorithm for PBKDF2 password derivation and two keyfiles, .Pa one.key and .Pa two.key : .Bd -ragged -offset indent .Nm Fl -create .Fl -device Ns = Ns Cm /dev/vn0 .Fl -cipher Ns = Ns Cm AES-256-XTS,TWOFISH-256-XTS .Fl -pbkdf-prf Ns = Ns Cm whirlpool .Fl -keyfile Ns = Ns Cm one.key .Fl -keyfile Ns = Ns Cm two.key .Ed .Pp Map the outer volume on the TrueCrypt volume on .Pa /dev/vn0 as .Sy truecrypt1 , but protect the hidden volume, using the keyfile .Pa hidden.key , from being overwritten: .Bd -ragged -offset indent .Nm Fl -map Ns = Ns Cm truecrypt1 .Fl -device Ns = Ns Cm /dev/vn0 .Fl -protect-hidden .Fl -keyfile-hidden Ns = Ns Cm hidden.key .Ed .Pp Map the hidden volume on the TrueCrypt volume on .Pa /dev/vn0 as .Sy truecrypt2 , using the keyfile .Pa hidden.key : .Bd -ragged -offset indent .Nm Fl -map Ns = Ns Cm truecrypt2 .Fl -device Ns = Ns Cm /dev/vn0 .Fl -keyfile Ns = Ns Cm hidden.key .Ed .Pp Map and mount the volume in the file .Pa secvol on Linux: .Bd -ragged -offset indent .Sy losetup Cm /dev/loop1 Cm secvol .Ed .Bd -ragged -offset indent .Nm Fl -map Ns = Ns Cm secv .Fl -device Ns = Ns Cm /dev/loop1 .Ed .Bd -ragged -offset indent .Sy mount Cm /dev/mapper/secv Cm /mnt .Ed .Pp Similarly on .Dx : .Bd -ragged -offset indent .Sy vnconfig Cm vn1 Cm secvol .Ed .Bd -ragged -offset indent .Nm Fl -map Ns = Ns Cm secv .Fl -device Ns = Ns Cm /dev/vn1 .Ed .Bd -ragged -offset indent .Sy mount Cm /dev/mapper/secv Cm /mnt .Ed .Pp Unmapping the volume .Sy truecrypt2 on both Linux and .Dx after unmounting: .Bd -ragged -offset indent .Sy dmsetup Cm remove Cm truecrypt2 .Ed .Pp Or alternatively: .Bd -ragged -offset indent .Nm Fl -unmap Ns = Ns Cm truecrypt2 .Ed .Pp A hidden volume whose existance can be plausibly denied and its outer volume can for example be created with .Bd -ragged -offset indent .Nm Fl -create .Fl -hidden .Fl -device Ns = Ns Cm /dev/loop0 .Fl -cipher Ns = Ns Cm AES-256-XTS,TWOFISH-256-XTS .Fl -pbkdf-prf Ns = Ns Cm whirlpool .Fl -keyfile Ns = Ns Cm one.key .Fl -cipher-hidden Ns = Ns Cm AES-256-XTS .Fl -pbkdf-prf-hidden Ns = Ns Cm whirlpool .Fl -keyfile-hidden Ns = Ns Cm hidden.key .Ed .Pp .Nm will prompt the user for the passphrase for both the outer and hidden volume as well as the size of the hidden volume inside the outer volume. The hidden volume will be created inside the area spanned by the outer volume. The hidden volume can optionally use a different cipher and prf function as specified by the .Fl -cipher-hidden and .Fl -pbkdf-prf-hidden options. Which volume is later accessed depends only on which passphrase and keyfile(s) are being used, so that the existance of the hidden volume remains unknown without knowledge of the passphrase and keyfile it is protected by since it is located within the outer volume. To map the outer volume without potentially damaging the hidden volume, the passphrase and keyfile(s) of the hidden volume must be known and provided alongside the .Fl -protect-hidden option. .Pp A disk encrypted using full disk encryption can be mapped using .Bd -ragged -offset indent .Nm Fl -map Ns = Ns Cm tcplay_sdb .Fl -device Ns = Ns Cm /dev/sdb .Fl -fde .Ed .Pp To access individual partitions on the now mapped disk, the following command will generate mappings for each individual partition on the encrypted disk: .Bd -ragged -offset indent .Sy kpartx Fl -av Cm /dev/mapper/tcplay_sdb .Ed .Sh SEE ALSO .Xr crypttab 5 , .Xr cryptsetup 8 , .Xr dmsetup 8 , .Xr kpartx 8 .Sh HISTORY The .Nm utility appeared in .Dx 2.11 . .Sh AUTHORS .An Alex Hornung tc-play-1.1/tcplay.c000066400000000000000000001325041217612412400143710ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define _BSD_SOURCE #include #include #if defined(__DragonFly__) #include #endif #include #include #include #include #include #include #include #include #include #if defined(__linux__) #include #include #elif defined(__DragonFly__) #include #include #endif #include #include "crc32.h" #include "tcplay.h" #include "humanize.h" /* XXX TODO: * - LRW-benbi support? needs further work in dm-crypt and even opencrypto * - secure buffer review (i.e: is everything that needs it using secure mem?) * - mlockall? (at least MCL_FUTURE, which is the only one we support) */ summary_fn_t summary_fn = NULL; int tc_internal_verbose = 1; char tc_internal_log_buffer[LOG_BUFFER_SZ]; int tc_internal_state = STATE_UNKNOWN; void tc_log(int is_err, const char *fmt, ...) { va_list ap; FILE *fp; if (is_err) fp = stderr; else fp = stdout; va_start(ap, fmt); vsnprintf(tc_internal_log_buffer, LOG_BUFFER_SZ, fmt, ap); va_end(ap); if (tc_internal_verbose) fprintf(fp, "%s", tc_internal_log_buffer); } /* Supported algorithms */ struct pbkdf_prf_algo pbkdf_prf_algos[] = { { "RIPEMD160", 2000 }, /* needs to come before the other RIPEMD160 */ { "RIPEMD160", 1000 }, { "SHA512", 1000 }, { "whirlpool", 1000 }, { NULL, 0 } }; struct tc_crypto_algo tc_crypto_algos[] = { #if 0 /* XXX: turns out TC doesn't support AES-128-XTS */ { "AES-128-XTS", "aes-xts-plain", 32, 8 }, { "TWOFISH-128-XTS", "twofish-xts-plain", 32, 8 }, { "SERPENT-128-XTS", "serpent-xts-plain", 32, 8 }, #endif { "AES-256-XTS", "aes-xts-plain64", 64, 8 }, { "TWOFISH-256-XTS", "twofish-xts-plain64", 64, 8 }, { "SERPENT-256-XTS", "serpent-xts-plain64", 64, 8 }, { NULL, NULL, 0, 0 } }; const char *valid_cipher_chains[][MAX_CIPHER_CHAINS] = { { "AES-256-XTS", NULL }, { "TWOFISH-256-XTS", NULL }, { "SERPENT-256-XTS", NULL }, { "AES-256-XTS", "TWOFISH-256-XTS", "SERPENT-256-XTS", NULL }, { "SERPENT-256-XTS", "TWOFISH-256-XTS", "AES-256-XTS", NULL }, #if 0 /* It seems that all the two-way cascades are the other way round... */ { "AES-256-XTS", "TWOFISH-256-XTS", NULL }, { "SERPENT-256-XTS", "AES-256-XTS", NULL }, { "TWOFISH-256-XTS", "SERPENT-256-XTS", NULL }, #endif { "TWOFISH-256-XTS", "AES-256-XTS", NULL }, { "AES-256-XTS", "SERPENT-256-XTS", NULL }, { "SERPENT-256-XTS", "TWOFISH-256-XTS", NULL }, { NULL } }; struct tc_cipher_chain *tc_cipher_chains[MAX_CIPHER_CHAINS]; static int tc_build_cipher_chains(void) { struct tc_cipher_chain *chain, *elem, *prev; int i = 0; int k; while (valid_cipher_chains[i][0] != NULL) { chain = NULL; prev = NULL; k = 0; while (valid_cipher_chains[i][k] != NULL) { if ((elem = alloc_safe_mem(sizeof(*elem))) == NULL) { tc_log(1, "Error allocating memory for " "cipher chain\n"); return -1; } /* Initialize first element of chain */ if (chain == NULL) { chain = elem; elem->prev = NULL; } /* Populate previous element */ if (prev != NULL) { prev->next = elem; elem->prev = prev; } /* Assume we are the last element in the chain */ elem->next = NULL; /* Initialize other fields */ elem->cipher = check_cipher(valid_cipher_chains[i][k], 0); if (elem->cipher == NULL) return -1; elem->key = NULL; prev = elem; ++k; } /* Store cipher chain */ tc_cipher_chains[i++] = chain; /* Integrity check */ if (i >= MAX_CIPHER_CHAINS) { tc_log(1, "FATAL: tc_cipher_chains is full!!\n"); return -1; } /* Make sure array is NULL terminated */ tc_cipher_chains[i] = NULL; } return 0; } static struct tc_cipher_chain * tc_dup_cipher_chain(struct tc_cipher_chain *src) { struct tc_cipher_chain *first = NULL, *prev = NULL, *elem; for (; src != NULL; src = src->next) { if ((elem = alloc_safe_mem(sizeof(*elem))) == NULL) { tc_log(1, "Error allocating memory for " "duplicate cipher chain\n"); return NULL; } memcpy(elem, src, sizeof(*elem)); if (src->key != NULL) { if ((elem->key = alloc_safe_mem(src->cipher->klen)) == NULL) { tc_log(1, "Error allocating memory for " "duplicate key in cipher chain\n"); return NULL; } memcpy(elem->key, src->key, src->cipher->klen); } if (first == NULL) first = elem; elem->next = NULL; elem->prev = prev; if (prev != NULL) prev->next = elem; prev = elem; } return first; } static int tc_free_cipher_chain(struct tc_cipher_chain *chain) { struct tc_cipher_chain *next = chain; while ((chain = next) != NULL) { next = chain->next; if (chain->key != NULL) free_safe_mem(chain->key); free_safe_mem(chain); } return 0; } static int tc_cipher_chain_length(struct tc_cipher_chain *chain) { int len = 0; for (; chain != NULL; chain = chain->next) ++len; return len; } int tc_cipher_chain_klen(struct tc_cipher_chain *chain) { int klen_bytes = 0; for (; chain != NULL; chain = chain->next) { klen_bytes += chain->cipher->klen; } return klen_bytes; } char * tc_cipher_chain_sprint(char *buf, size_t bufsz, struct tc_cipher_chain *chain) { static char sbuf[256]; int n = 0; if (buf == NULL) { buf = sbuf; bufsz = sizeof(sbuf); } for (; chain != NULL; chain = chain->next) { n += snprintf(buf+n, bufsz-n, "%s%s", chain->cipher->name, (chain->next != NULL) ? "," : "\0"); } return buf; } #ifdef DEBUG static void print_hex(unsigned char *buf, off_t start, size_t len) { size_t i; for (i = start; i < start+len; i++) printf("%02x", buf[i]); printf("\n"); } #endif void print_info(struct tcplay_info *info) { printf("Device:\t\t\t%s\n", info->dev); if (info->pbkdf_prf != NULL) { printf("PBKDF2 PRF:\t\t%s\n", info->pbkdf_prf->name); printf("PBKDF2 iterations:\t%d\n", info->pbkdf_prf->iteration_count); } printf("Cipher:\t\t\t%s\n", tc_cipher_chain_sprint(NULL, 0, info->cipher_chain)); printf("Key Length:\t\t%d bits\n", 8*tc_cipher_chain_klen(info->cipher_chain)); if (info->hdr != NULL) { printf("CRC Key Data:\t\t%#x\n", info->hdr->crc_keys); printf("Sector size:\t\t%d\n", info->hdr->sec_sz); } else { printf("Sector size:\t\t512\n"); } printf("Volume size:\t\t%zu sectors\n", info->size); #if 0 /* Don't print this; it's always 0 and is rather confusing */ printf("Volume offset:\t\t%"PRIu64"\n", (uint64_t)info->start); #endif #ifdef DEBUG printf("Vol Flags:\t\t%d\n", info->volflags); #endif printf("IV offset:\t\t%"PRIu64"\n", (uint64_t)info->skip); printf("Block offset:\t\t%"PRIu64"\n", (uint64_t)info->offset); } static struct tcplay_info * new_info(const char *dev, int flags, struct tc_cipher_chain *cipher_chain, struct pbkdf_prf_algo *prf, struct tchdr_dec *hdr, off_t start) { struct tc_cipher_chain *chain_start; struct tcplay_info *info; int i; int error; chain_start = cipher_chain; if ((info = (struct tcplay_info *)alloc_safe_mem(sizeof(*info))) == NULL) { tc_log(1, "could not allocate safe info memory\n"); return NULL; } strncpy(info->dev, dev, sizeof(info->dev)); info->cipher_chain = cipher_chain; info->pbkdf_prf = prf; info->start = start; info->hdr = hdr; info->size = hdr->sz_mk_scope / hdr->sec_sz; /* volume size */ info->skip = hdr->off_mk_scope / hdr->sec_sz; /* iv skip */ info->volflags = hdr->flags; info->flags = flags; if (TC_FLAG_SET(flags, SYS)) info->offset = 0; /* offset is 0 for system volumes */ else info->offset = hdr->off_mk_scope / hdr->sec_sz; /* block offset */ /* Associate a key out of the key pool with each cipher in the chain */ error = tc_cipher_chain_populate_keys(cipher_chain, hdr->keys); if (error) { tc_log(1, "could not populate keys in cipher chain\n"); return NULL; } for (; cipher_chain != NULL; cipher_chain = cipher_chain->next) { for (i = 0; i < cipher_chain->cipher->klen; i++) sprintf(&cipher_chain->dm_key[i*2], "%02x", cipher_chain->key[i]); } tc_cipher_chain_free_keys(chain_start); return info; } static int free_info(struct tcplay_info *info) { if (info->cipher_chain) tc_free_cipher_chain(info->cipher_chain); if (info->hdr) free_safe_mem(info->hdr); free_safe_mem(info); return 0; } int adjust_info(struct tcplay_info *info, struct tcplay_info *hinfo) { if (hinfo->hdr->sz_hidvol == 0) return 1; info->size -= hinfo->hdr->sz_hidvol / hinfo->hdr->sec_sz; return 0; } int process_hdr(const char *dev, int flags, unsigned char *pass, int passlen, struct tchdr_enc *ehdr, struct tcplay_info **pinfo) { struct tchdr_dec *dhdr; struct tcplay_info *info; struct tc_cipher_chain *cipher_chain = NULL; unsigned char *key; int i, j, found, error; *pinfo = NULL; if ((key = alloc_safe_mem(MAX_KEYSZ)) == NULL) { tc_log(1, "could not allocate safe key memory\n"); return ENOMEM; } /* Start search for correct algorithm combination */ found = 0; for (i = 0; !found && pbkdf_prf_algos[i].name != NULL; i++) { #ifdef DEBUG printf("\nTrying PRF algo %s (%d)\n", pbkdf_prf_algos[i].name, pbkdf_prf_algos[i].iteration_count); printf("Salt: "); print_hex(ehdr->salt, 0, sizeof(ehdr->salt)); #endif error = pbkdf2(&pbkdf_prf_algos[i], (char *)pass, passlen, ehdr->salt, sizeof(ehdr->salt), MAX_KEYSZ, key); if (error) { tc_log(1, "pbkdf failed for algorithm %s\n", pbkdf_prf_algos[i].name); free_safe_mem(key); return EINVAL; } #if 0 printf("Derived Key: "); print_hex(key, 0, MAX_KEYSZ); #endif for (j = 0; !found && tc_cipher_chains[j] != NULL; j++) { cipher_chain = tc_dup_cipher_chain(tc_cipher_chains[j]); #ifdef DEBUG printf("\nTrying cipher chain %d\n", j); #endif dhdr = decrypt_hdr(ehdr, cipher_chain, key); if (dhdr == NULL) { tc_log(1, "hdr decryption failed for cipher " "chain %d\n", j); free_safe_mem(key); return EINVAL; } if (verify_hdr(dhdr)) { #ifdef DEBUG printf("tc_str: %.4s, tc_ver: %d, tc_min_ver: %d, " "crc_keys: %d, sz_vol: %"PRIu64", " "off_mk_scope: %"PRIu64", sz_mk_scope: %"PRIu64", " "flags: %d, sec_sz: %d crc_dhdr: %d\n", dhdr->tc_str, dhdr->tc_ver, dhdr->tc_min_ver, dhdr->crc_keys, dhdr->sz_vol, dhdr->off_mk_scope, dhdr->sz_mk_scope, dhdr->flags, dhdr->sec_sz, dhdr->crc_dhdr); #endif found = 1; } else { free_safe_mem(dhdr); tc_free_cipher_chain(cipher_chain); } } } free_safe_mem(key); if (!found) return EINVAL; if ((info = new_info(dev, flags, cipher_chain, &pbkdf_prf_algos[i-1], dhdr, 0)) == NULL) { free_safe_mem(dhdr); return ENOMEM; } *pinfo = info; return 0; } int create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles, struct pbkdf_prf_algo *prf_algo, struct tc_cipher_chain *cipher_chain, struct pbkdf_prf_algo *h_prf_algo, struct tc_cipher_chain *h_cipher_chain, const char *passphrase, const char *h_passphrase, size_t size_hidden_bytes_in, int interactive, int use_secure_erase, int weak_keys) { char *pass, *pass_again; char *h_pass = NULL; char buf[1024]; size_t blocks, blksz, hidden_blocks = 0; struct tchdr_enc *ehdr, *hehdr; struct tchdr_enc *ehdr_backup, *hehdr_backup; uint64_t tmp; int error, r, ret; pass = h_pass = pass_again = NULL; ehdr = hehdr = NULL; ehdr_backup = hehdr_backup = NULL; ret = -1; /* Default to returning error */ if (cipher_chain == NULL) cipher_chain = tc_cipher_chains[0]; if (prf_algo == NULL) prf_algo = &pbkdf_prf_algos[0]; if (h_cipher_chain == NULL) h_cipher_chain = cipher_chain; if (h_prf_algo == NULL) h_prf_algo = prf_algo; if ((error = get_disk_info(dev, &blocks, &blksz)) != 0) { tc_log(1, "could not get disk info\n"); return -1; } if ((blocks*blksz) <= MIN_VOL_BYTES) { tc_log(1, "Cannot create volumes on devices with less " "than %d bytes\n", MIN_VOL_BYTES); return -1; } if (interactive) { if (((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) || ((pass_again = alloc_safe_mem(PASS_BUFSZ)) == NULL)) { tc_log(1, "could not allocate safe passphrase memory\n"); goto out; } if ((error = read_passphrase("Passphrase: ", pass, MAX_PASSSZ, PASS_BUFSZ, 0) || (read_passphrase("Repeat passphrase: ", pass_again, MAX_PASSSZ, PASS_BUFSZ, 0)))) { tc_log(1, "could not read passphrase\n"); goto out; } if (strcmp(pass, pass_again) != 0) { tc_log(1, "Passphrases don't match\n"); goto out; } free_safe_mem(pass_again); pass_again = NULL; } else { /* In batch mode, use provided passphrase */ if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe " "passphrase memory"); goto out; } if (passphrase != NULL) { strncpy(pass, passphrase, MAX_PASSSZ); pass[MAX_PASSSZ] = '\0'; } } if (nkeyfiles > 0) { /* Apply keyfiles to 'pass' */ if ((error = apply_keyfiles((unsigned char *)pass, PASS_BUFSZ, keyfiles, nkeyfiles))) { tc_log(1, "could not apply keyfiles\n"); goto out; } } if (hidden) { if (interactive) { if (((h_pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) || ((pass_again = alloc_safe_mem(PASS_BUFSZ)) == NULL)) { tc_log(1, "could not allocate safe " "passphrase memory\n"); goto out; } if ((error = read_passphrase("Passphrase for hidden volume: ", h_pass, MAX_PASSSZ, PASS_BUFSZ, 0) || (read_passphrase("Repeat passphrase: ", pass_again, MAX_PASSSZ, PASS_BUFSZ, 0)))) { tc_log(1, "could not read passphrase\n"); goto out; } if (strcmp(h_pass, pass_again) != 0) { tc_log(1, "Passphrases for hidden volume don't " "match\n"); goto out; } free_safe_mem(pass_again); pass_again = NULL; } else { /* In batch mode, use provided passphrase */ if ((h_pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe " "passphrase memory"); goto out; } if (h_passphrase != NULL) { strncpy(h_pass, h_passphrase, MAX_PASSSZ); h_pass[MAX_PASSSZ] = '\0'; } } if (n_hkeyfiles > 0) { /* Apply keyfiles to 'h_pass' */ if ((error = apply_keyfiles((unsigned char *)h_pass, PASS_BUFSZ, h_keyfiles, n_hkeyfiles))) { tc_log(1, "could not apply keyfiles\n"); goto out; } } if (interactive) { hidden_blocks = 0; } else { hidden_blocks = size_hidden_bytes_in/blksz; if (hidden_blocks == 0) { tc_log(1, "hidden_blocks to create volume " "cannot be zero!\n"); goto out; } if (size_hidden_bytes_in >= (blocks*blksz) - MIN_VOL_BYTES) { tc_log(1, "Hidden volume needs to be " "smaller than the outer volume\n"); goto out; } } /* This only happens in interactive mode */ while (hidden_blocks == 0) { if ((r = _humanize_number(buf, sizeof(buf), (uint64_t)(blocks * blksz))) < 0) { sprintf(buf, "%zu bytes", (blocks * blksz)); } printf("The total volume size of %s is %s (bytes)\n", dev, buf); memset(buf, 0, sizeof(buf)); printf("Size of hidden volume (e.g. 127M): "); fflush(stdout); if ((fgets(buf, sizeof(buf), stdin)) == NULL) { tc_log(1, "Could not read from stdin\n"); goto out; } /* get rid of trailing newline */ buf[strlen(buf)-1] = '\0'; if ((error = _dehumanize_number(buf, &tmp)) != 0) { tc_log(1, "Could not interpret input: %s\n", buf); continue; } if (tmp >= (blocks*blksz) - MIN_VOL_BYTES) { tc_log(1, "Hidden volume needs to be " "smaller than the outer volume\n"); hidden_blocks = 0; continue; } hidden_blocks = (size_t)tmp; hidden_blocks /= blksz; } } if (interactive) { /* Show summary and ask for confirmation */ printf("Summary of actions:\n"); printf(" - Completely erase *EVERYTHING* on %s\n", dev); printf(" - Create %svolume on %s\n", hidden?("outer "):"", dev); if (hidden) { printf(" - Create hidden volume of %zu bytes at end of " "outer volume\n", hidden_blocks * blksz); } printf("\n Are you sure you want to proceed? (y/n) "); fflush(stdout); if ((fgets(buf, sizeof(buf), stdin)) == NULL) { tc_log(1, "Could not read from stdin\n"); goto out; } if ((buf[0] != 'y') && (buf[0] != 'Y')) { tc_log(1, "User cancelled action(s)\n"); goto out; } } /* erase volume */ if (use_secure_erase) { tc_log(0, "Securely erasing the volume...\nThis process may take " "some time depending on the size of the volume\n"); if ((error = secure_erase(dev, blocks * blksz, blksz)) != 0) { tc_log(1, "could not securely erase device %s\n", dev); goto out; } } tc_log(0, "Creating volume headers...\nDepending on your system, this " "process may take a few minutes as it uses true random data which " "might take a while to refill\n"); if (weak_keys) { tc_log(0, "WARNING: Using a weak random generator to get " "entropy for the key material. Odds are this is NOT " "what you want.\n"); } /* create encrypted headers */ ehdr = create_hdr((unsigned char *)pass, (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass), prf_algo, cipher_chain, blksz, blocks, VOL_RSVD_BYTES_START/blksz, blocks - (MIN_VOL_BYTES/blksz), 0, weak_keys, &ehdr_backup); if (ehdr == NULL) { tc_log(1, "Could not create header\n"); goto out; } if (hidden) { hehdr = create_hdr((unsigned char *)h_pass, (n_hkeyfiles > 0)?MAX_PASSSZ:strlen(h_pass), h_prf_algo, h_cipher_chain, blksz, blocks, blocks - (VOL_RSVD_BYTES_END/blksz) - hidden_blocks, hidden_blocks, 1, weak_keys, &hehdr_backup); if (hehdr == NULL) { tc_log(1, "Could not create hidden volume header\n"); goto out; } } tc_log(0, "Writing volume headers to disk...\n"); if ((error = write_to_disk(dev, 0, blksz, ehdr, sizeof(*ehdr))) != 0) { tc_log(1, "Could not write volume header to device\n"); goto out; } /* Write backup header; it's offset is relative to the end */ if ((error = write_to_disk(dev, (blocks*blksz - BACKUP_HDR_OFFSET_END), blksz, ehdr_backup, sizeof(*ehdr_backup))) != 0) { tc_log(1, "Could not write backup volume header to device\n"); goto out; } if (hidden) { if ((error = write_to_disk(dev, HDR_OFFSET_HIDDEN, blksz, hehdr, sizeof(*hehdr))) != 0) { tc_log(1, "Could not write hidden volume header to " "device\n"); goto out; } /* Write backup hidden header; offset is relative to end */ if ((error = write_to_disk(dev, (blocks*blksz - BACKUP_HDR_HIDDEN_OFFSET_END), blksz, hehdr_backup, sizeof(*hehdr_backup))) != 0) { tc_log(1, "Could not write backup hidden volume " "header to device\n"); goto out; } } /* Everything went ok */ tc_log(0, "All done!\n"); ret = 0; out: if (pass) free_safe_mem(pass); if (h_pass) free_safe_mem(h_pass); if (pass_again) free_safe_mem(pass_again); if (ehdr) free_safe_mem(ehdr); if (hehdr) free_safe_mem(hehdr); if (ehdr_backup) free_safe_mem(ehdr_backup); if (hehdr_backup) free_safe_mem(hehdr_backup); return ret; } struct tcplay_info * info_map_common(const char *dev, int flags, const char *sys_dev, int protect_hidden, const char *keyfiles[], int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles, const char *passphrase, const char *passphrase_hidden, int interactive, int retries, time_t timeout, char *passphrase_out) { struct tchdr_enc *ehdr, *hehdr = NULL; struct tcplay_info *info, *hinfo = NULL; char *pass; char *h_pass; int error, error2 = 0; size_t sz; size_t blocks, blksz; int is_hidden = 0; if ((error = get_disk_info(dev, &blocks, &blksz)) != 0) { tc_log(1, "could not get disk information\n"); return NULL; } if (retries < 1) retries = 1; info = NULL; ehdr = NULL; pass = h_pass = NULL; while ((info == NULL) && retries-- > 0) { pass = h_pass = NULL; ehdr = hehdr = NULL; info = hinfo = NULL; if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe passphrase memory\n"); goto out; } if (interactive) { if ((error = read_passphrase("Passphrase: ", pass, MAX_PASSSZ, PASS_BUFSZ, timeout))) { tc_log(1, "could not read passphrase\n"); /* XXX: handle timeout differently? */ goto out; } pass[MAX_PASSSZ] = '\0'; } else { /* In batch mode, use provided passphrase */ if (passphrase != NULL) { strncpy(pass, passphrase, MAX_PASSSZ); pass[MAX_PASSSZ] = '\0'; } } if (passphrase_out != NULL) { strcpy(passphrase_out, pass); } if (nkeyfiles > 0) { /* Apply keyfiles to 'pass' */ if ((error = apply_keyfiles((unsigned char *)pass, PASS_BUFSZ, keyfiles, nkeyfiles))) { tc_log(1, "could not apply keyfiles"); goto out; } } if (protect_hidden) { if ((h_pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe passphrase memory\n"); goto out; } if (interactive) { if ((error = read_passphrase( "Passphrase for hidden volume: ", h_pass, MAX_PASSSZ, PASS_BUFSZ, timeout))) { tc_log(1, "could not read passphrase\n"); goto out; } h_pass[MAX_PASSSZ] = '\0'; } else { /* In batch mode, use provided passphrase */ if (passphrase_hidden != NULL) { strncpy(h_pass, passphrase_hidden, MAX_PASSSZ); h_pass[MAX_PASSSZ] = '\0'; } } if (n_hkeyfiles > 0) { /* Apply keyfiles to 'pass' */ if ((error = apply_keyfiles((unsigned char *)h_pass, PASS_BUFSZ, h_keyfiles, n_hkeyfiles))) { tc_log(1, "could not apply keyfiles"); goto out; } } } /* Always read blksz-sized chunks */ sz = blksz; ehdr = (struct tchdr_enc *)read_to_safe_mem( (TC_FLAG_SET(flags, SYS)) ? sys_dev : dev, (TC_FLAG_SET(flags, SYS) || TC_FLAG_SET(flags, FDE)) ? HDR_OFFSET_SYS : (!TC_FLAG_SET(flags, BACKUP)) ? 0 : -BACKUP_HDR_OFFSET_END, &sz); if (ehdr == NULL) { tc_log(1, "error read hdr_enc: %s", dev); goto out; } if (!TC_FLAG_SET(flags, SYS)) { /* Always read blksz-sized chunks */ sz = blksz; hehdr = (struct tchdr_enc *)read_to_safe_mem(dev, (!TC_FLAG_SET(flags, BACKUP)) ? HDR_OFFSET_HIDDEN : -BACKUP_HDR_HIDDEN_OFFSET_END, &sz); if (hehdr == NULL) { tc_log(1, "error read hdr_enc: %s", dev); goto out; } } else { hehdr = NULL; } error = process_hdr(dev, flags, (unsigned char *)pass, (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass), ehdr, &info); /* * Try to process hidden header if we have to protect the hidden * volume, or the decryption/verification of the main header * failed. */ if (hehdr && (error || protect_hidden)) { if (error) { error2 = process_hdr(dev, flags, (unsigned char *)pass, (nkeyfiles > 0)?MAX_PASSSZ:strlen(pass), hehdr, &info); is_hidden = !error2; } else if (protect_hidden) { error2 = process_hdr(dev, flags, (unsigned char *)h_pass, (n_hkeyfiles > 0)?MAX_PASSSZ:strlen(h_pass), hehdr, &hinfo); } } /* We need both to protect a hidden volume */ if ((protect_hidden && (error || error2)) || (error && error2)) { tc_log(1, "Incorrect password or not a TrueCrypt volume\n"); if (info) { free_info(info); info = NULL; } if (hinfo) { free_info(hinfo); hinfo = NULL; } /* Try again (or finish) */ free_safe_mem(pass); pass = NULL; if (h_pass) { free_safe_mem(h_pass); h_pass = NULL; } if (ehdr) { free_safe_mem(ehdr); ehdr = NULL; } if (hehdr) { free_safe_mem(hehdr); hehdr = NULL; } continue; } if (protect_hidden) { if (adjust_info(info, hinfo) != 0) { tc_log(1, "Could not protect hidden volume\n"); if (info) free_info(info); info = NULL; if (hinfo) free_info(hinfo); hinfo = NULL; goto out; } if (hinfo) { free_info(hinfo); hinfo = NULL; } } } out: if (hinfo) free_info(hinfo); if (pass) free_safe_mem(pass); if (h_pass) free_safe_mem(h_pass); if (ehdr) free_safe_mem(ehdr); if (hehdr) free_safe_mem(hehdr); if (info != NULL) info->hidden = is_hidden; return info; } int info_mapped_volume(const char *map_name, int interactive) { struct tcplay_info *info; info = dm_info_map(map_name); if (info != NULL) { if (interactive) print_info(info); free_info(info); return 0; /* NOT REACHED */ } else if (interactive) { tc_log(1, "Could not retrieve information about mapped " "volume %s. Does it exist?\n", map_name); } return -1; } int info_volume(const char *device, int flags, const char *sys_dev, int protect_hidden, const char *keyfiles[], int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles, const char *passphrase, const char *passphrase_hidden, int interactive, int retries, time_t timeout) { struct tcplay_info *info; info = info_map_common(device, flags, sys_dev, protect_hidden, keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, passphrase, passphrase_hidden, interactive, retries, timeout, NULL); if (info != NULL) { if (interactive) print_info(info); free_info(info); return 0; /* NOT REACHED */ } return -1; } int map_volume(const char *map_name, const char *device, int flags, const char *sys_dev, int protect_hidden, const char *keyfiles[], int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles, const char *passphrase, const char *passphrase_hidden, int interactive, int retries, time_t timeout) { struct tcplay_info *info; int error; info = info_map_common(device, flags, sys_dev, protect_hidden, keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, passphrase, passphrase_hidden, interactive, retries, timeout, NULL); if (info == NULL) return -1; if ((error = dm_setup(map_name, info)) != 0) { tc_log(1, "Could not set up mapping %s\n", map_name); free_info(info); return -1; } if (interactive) printf("All ok!\n"); free_info(info); return 0; } int modify_volume(const char *device, int flags, const char *sys_dev, const char *keyfiles[], int nkeyfiles, const char *new_keyfiles[], int n_newkeyfiles, struct pbkdf_prf_algo *new_prf_algo, const char *passphrase, const char *new_passphrase, int interactive, int retries, time_t timeout, int weak_salt) { struct tcplay_info *info; struct tchdr_enc *ehdr, *ehdr_backup; char *pass, *pass_again; int ret = -1; off_t offset, offset_backup; const char *dev; size_t blocks, blksz; int error; ehdr = ehdr_backup = NULL; pass = pass_again = NULL; info = NULL; if (TC_FLAG_SET(flags, ONLY_RESTORE)) { if (interactive) { if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe " "passphrase memory"); goto out; } } else { new_passphrase = passphrase; } new_keyfiles = keyfiles; n_newkeyfiles = nkeyfiles; new_prf_algo = NULL; } info = info_map_common(device, flags, sys_dev, 0, keyfiles, nkeyfiles, NULL, 0, passphrase, NULL, interactive, retries, timeout, pass); if (info == NULL) goto out; if (interactive && !TC_FLAG_SET(flags, ONLY_RESTORE)) { if (((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) || ((pass_again = alloc_safe_mem(PASS_BUFSZ)) == NULL)) { tc_log(1, "could not allocate safe passphrase memory\n"); goto out; } if ((error = read_passphrase("New passphrase: ", pass, MAX_PASSSZ, PASS_BUFSZ, 0) || (read_passphrase("Repeat passphrase: ", pass_again, MAX_PASSSZ, PASS_BUFSZ, 0)))) { tc_log(1, "could not read passphrase\n"); goto out; } if (strcmp(pass, pass_again) != 0) { tc_log(1, "Passphrases don't match\n"); goto out; } free_safe_mem(pass_again); pass_again = NULL; } else if (!interactive) { /* In batch mode, use provided passphrase */ if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) { tc_log(1, "could not allocate safe " "passphrase memory"); goto out; } if (new_passphrase != NULL) { strncpy(pass, new_passphrase, MAX_PASSSZ); pass[MAX_PASSSZ] = '\0'; } } if (n_newkeyfiles > 0) { /* Apply keyfiles to 'pass' */ if ((error = apply_keyfiles((unsigned char *)pass, PASS_BUFSZ, new_keyfiles, n_newkeyfiles))) { tc_log(1, "could not apply keyfiles\n"); goto out; } } ehdr = copy_reencrypt_hdr((unsigned char *)pass, (n_newkeyfiles > 0)?MAX_PASSSZ:strlen(pass), new_prf_algo, weak_salt, info, &ehdr_backup); if (ehdr == NULL) { tc_log(1, "Could not create header\n"); goto out; } dev = (TC_FLAG_SET(flags, SYS)) ? sys_dev : device; if (TC_FLAG_SET(flags, SYS) || TC_FLAG_SET(flags, FDE)) { /* SYS and FDE don't have backup headers (as far as I understand) */ if (info->hidden) { offset = HDR_OFFSET_HIDDEN; } else { offset = HDR_OFFSET_SYS; } } else { if (info->hidden) { offset = HDR_OFFSET_HIDDEN; offset_backup = -BACKUP_HDR_HIDDEN_OFFSET_END; } else { offset = 0; offset_backup = -BACKUP_HDR_OFFSET_END; } } if ((error = get_disk_info(dev, &blocks, &blksz)) != 0) { tc_log(1, "could not get disk information\n"); goto out; } tc_log(0, "Writing new volume headers to disk...\n"); if ((error = write_to_disk(dev, offset, blksz, ehdr, sizeof(*ehdr))) != 0) { tc_log(1, "Could not write volume header to device\n"); goto out; } if (!TC_FLAG_SET(flags, SYS) && !TC_FLAG_SET(flags, FDE)) { if ((error = write_to_disk(dev, offset_backup, blksz, ehdr_backup, sizeof(*ehdr_backup))) != 0) { tc_log(1, "Could not write backup volume header to device\n"); goto out; } } /* Everything went ok */ tc_log(0, "All done!\n"); ret = 0; out: if (pass) free_safe_mem(pass); if (pass_again) free_safe_mem(pass_again); if (ehdr) free_safe_mem(ehdr); if (ehdr_backup) free_safe_mem(ehdr_backup); if (info) free_safe_mem(info); return ret; } static int dm_get_info(const char *name, struct dm_info *dmi) { struct dm_task *dmt = NULL; int error = -1; if ((dmt = dm_task_create(DM_DEVICE_INFO)) == NULL) goto out; if ((dm_task_set_name(dmt, name)) == 0) goto out; if ((dm_task_run(dmt)) == 0) goto out; if ((dm_task_get_info(dmt, dmi)) == 0) goto out; error = 0; out: if (dmt) dm_task_destroy(dmt); return error; } #if defined(__DragonFly__) static int xlate_maj_min(char *start_path __unused, int max_depth __unused, char *buf, size_t bufsz, uint32_t maj, uint32_t min) { dev_t dev = makedev(maj, min); snprintf(buf, bufsz, "/dev/%s", devname(dev, S_IFCHR)); return 1; } #else static int xlate_maj_min(const char *start_path, int max_depth, char *buf, size_t bufsz, uint32_t maj, uint32_t min) { dev_t dev = makedev(maj, min); char path[PATH_MAX]; struct stat sb; struct dirent *ent; DIR *dirp; int found = 0; if (max_depth <= 0) return -1; if ((dirp = opendir(start_path)) == NULL) return -1; while ((ent = readdir(dirp)) != NULL) { /* d_name, d_type, DT_BLK, DT_CHR, DT_DIR, DT_LNK */ if (ent->d_name[0] == '.') continue; /* Linux' /dev is littered with junk, so skip over it */ /* * The dm- devices seem to be the raw DM devices * things in mapper/ link to. */ if (((strcmp(ent->d_name, "block")) == 0) || ((strcmp(ent->d_name, "fd")) == 0) || (((strncmp(ent->d_name, "dm-", 3) == 0) && strlen(ent->d_name) <= 5))) continue; snprintf(path, PATH_MAX, "%s/%s", start_path, ent->d_name); if ((stat(path, &sb)) < 0) continue; if (S_ISDIR(sb.st_mode)) { found = !xlate_maj_min(path, max_depth-1, buf, bufsz, maj, min); if (found) break; } if (!S_ISBLK(sb.st_mode)) continue; if (sb.st_rdev != dev) continue; snprintf(buf, bufsz, "%s", path); found = 1; break; } if (dirp) closedir(dirp); return found ? 0 : -ENOENT; } #endif static struct tcplay_dm_table * dm_get_table(const char *name) { struct tcplay_dm_table *tc_table; struct dm_task *dmt = NULL; void *next = NULL; uint64_t start, length; char *target_type; char *params; char *p1; int c = 0; uint32_t maj, min; if ((tc_table = (struct tcplay_dm_table *)alloc_safe_mem(sizeof(*tc_table))) == NULL) { tc_log(1, "could not allocate safe tc_table memory\n"); return NULL; } if ((dmt = dm_task_create(DM_DEVICE_TABLE)) == NULL) goto error; if ((dm_task_set_name(dmt, name)) == 0) goto error; if ((dm_task_run(dmt)) == 0) goto error; tc_table->start = (off_t)0; tc_table->size = (size_t)0; do { next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); tc_table->size += (size_t)length; strncpy(tc_table->target, target_type, sizeof(tc_table->target)); /* Skip any leading whitespace */ while (params && *params == ' ') params++; if (strcmp(target_type, "crypt") == 0) { while ((p1 = strsep(¶ms, " ")) != NULL) { /* Skip any whitespace before the next strsep */ while (params && *params == ' ') params++; /* Process p1 */ if (c == 0) { /* cipher */ strncpy(tc_table->cipher, p1, sizeof(tc_table->cipher)); } else if (c == 2) { /* iv offset */ tc_table->skip = (off_t)strtoll(p1, NULL, 10); } else if (c == 3) { /* major:minor */ maj = strtoul(p1, NULL, 10); while (*p1 != ':' && *p1 != '\0') p1++; min = strtoul(++p1, NULL, 10); if ((xlate_maj_min("/dev", 2, tc_table->device, sizeof(tc_table->device), maj, min)) != 0) snprintf(tc_table->device, sizeof(tc_table->device), "%u:%u", maj, min); } else if (c == 4) { /* block offset */ tc_table->offset = (off_t)strtoll(p1, NULL, 10); } ++c; } if (c != 5) { tc_log(1, "could not get all the info required from " "the table\n"); goto error; } } } while (next != NULL); if (dmt) dm_task_destroy(dmt); #ifdef DEBUG printf("device: %s\n", tc_table->device); printf("target: %s\n", tc_table->target); printf("cipher: %s\n", tc_table->cipher); printf("size: %ju\n", tc_table->size); printf("offset: %"PRId64"\n", tc_table->offset); printf("skip: %"PRId64"\n", tc_table->skip); #endif return tc_table; error: if (dmt) dm_task_destroy(dmt); if (tc_table) free_safe_mem(tc_table); return NULL; } struct tcplay_info * dm_info_map(const char *map_name) { struct dm_task *dmt = NULL; struct dm_info dmi[3]; struct tcplay_dm_table *dm_table[3]; struct tc_crypto_algo *crypto_algo; struct tcplay_info *info; char map[PATH_MAX]; char ciphers[512]; int i, outermost = -1; memset(dm_table, 0, sizeof(dm_table)); if ((info = (struct tcplay_info *)alloc_safe_mem(sizeof(*info))) == NULL) { tc_log(1, "could not allocate safe info memory\n"); return NULL; } strncpy(map, map_name, PATH_MAX); for (i = 0; i < 3; i++) { if ((dm_get_info(map, &dmi[i])) != 0) goto error; if (dmi[i].exists) dm_table[i] = dm_get_table(map); snprintf(map, PATH_MAX, "%s.%d", map_name, i); } if (dmt) dm_task_destroy(dmt); if (dm_table[0] == NULL) goto error; /* * Process our dmi, dm_table fun into the info structure. */ /* First find which cipher chain we are using */ ciphers[0] = '\0'; for (i = 0; i < 3; i++) { if (dm_table[i] == NULL) continue; if (outermost < i) outermost = i; crypto_algo = &tc_crypto_algos[0]; while ((crypto_algo != NULL) && (strcmp(dm_table[i]->cipher, crypto_algo->dm_crypt_str) != 0)) ++crypto_algo; if (crypto_algo == NULL) { tc_log(1, "could not find corresponding cipher\n"); goto error; } strcat(ciphers, crypto_algo->name); strcat(ciphers, ","); } ciphers[strlen(ciphers)-1] = '\0'; info->cipher_chain = check_cipher_chain(ciphers, 1); if (info->cipher_chain == NULL) { tc_log(1, "could not find cipher chain\n"); goto error; } /* Copy over the name */ strncpy(info->dev, dm_table[outermost]->device, sizeof(info->dev)); /* Other fields */ info->hdr = NULL; info->pbkdf_prf = NULL; info->start = dm_table[outermost]->start; info->size = dm_table[0]->size; info->skip = dm_table[outermost]->skip; info->offset = dm_table[outermost]->offset; return info; error: if (dmt) dm_task_destroy(dmt); if (info) free_safe_mem(info); for (i = 0; i < 3; i++) if (dm_table[i] != NULL) free_safe_mem(dm_table[i]); return NULL; } static int dm_exists_device(const char *name) { struct dm_info dmi; int exists = 0; if (dm_get_info(name, &dmi) != 0) goto out; exists = dmi.exists; out: return exists; } static int dm_remove_device(const char *name) { struct dm_task *dmt = NULL; int ret = EINVAL; if ((dmt = dm_task_create(DM_DEVICE_REMOVE)) == NULL) goto out; if ((dm_task_set_name(dmt, name)) == 0) goto out; if ((dm_task_run(dmt)) == 0) goto out; ret = 0; out: if (dmt) dm_task_destroy(dmt); return ret; } int dm_setup(const char *mapname, struct tcplay_info *info) { struct tc_cipher_chain *cipher_chain; struct dm_task *dmt = NULL; struct dm_info dmi; char *params = NULL; char *uu, *uu_temp; char *uu_stack[64]; int uu_stack_idx; #if defined(__DragonFly__) uint32_t status; #endif int r, ret = 0; int j, len; off_t start, offset; char dev[PATH_MAX]; char map[PATH_MAX]; uint32_t cookie; dm_udev_set_sync_support(1); if ((params = alloc_safe_mem(512)) == NULL) { tc_log(1, "could not allocate safe parameters memory"); return ENOMEM; } strcpy(dev, info->dev); start = info->start; offset = info->offset; uu_stack_idx = 0; /* * Find length of cipher chain. Could use the for below, but doesn't * really matter. */ len = tc_cipher_chain_length(info->cipher_chain); /* Get to the end of the chain */ for (cipher_chain = info->cipher_chain; cipher_chain->next != NULL; cipher_chain = cipher_chain->next) ; /* * Start j at len-2, as we want to use .0, and the final one has no * suffix. */ for (j = len-2; cipher_chain != NULL; cipher_chain = cipher_chain->prev, j--) { cookie = 0; if ((dmt = dm_task_create(DM_DEVICE_CREATE)) == NULL) { tc_log(1, "dm_task_create failed\n"); ret = -1; goto out; } /* * If this is the last element in the cipher chain, use the * final map name. Otherwise pick a secondary name... */ if (cipher_chain->prev == NULL) strcpy(map, mapname); else sprintf(map, "%s.%d", mapname, j); if ((dm_task_set_name(dmt, map)) == 0) { tc_log(1, "dm_task_set_name failed\n"); ret = -1; goto out; } #if defined(__linux__) uuid_generate(info->uuid); if ((uu_temp = malloc(1024)) == NULL) { tc_log(1, "uuid_unparse memory failed\n"); ret = -1; goto out; } uuid_unparse(info->uuid, uu_temp); #elif defined(__DragonFly__) uuid_create(&info->uuid, &status); if (status != uuid_s_ok) { tc_log(1, "uuid_create failed\n"); ret = -1; goto out; } uuid_to_string(&info->uuid, &uu_temp, &status); if (uu_temp == NULL) { tc_log(1, "uuid_to_string failed\n"); ret = -1; goto out; } #endif if ((uu = malloc(1024)) == NULL) { free(uu_temp); tc_log(1, "uuid second malloc failed\n"); ret = -1; goto out; } snprintf(uu, 1024, "CRYPT-TCPLAY-%s", uu_temp); free(uu_temp); if ((dm_task_set_uuid(dmt, uu)) == 0) { free(uu); tc_log(1, "dm_task_set_uuid failed\n"); ret = -1; goto out; } free(uu); if (TC_FLAG_SET(info->flags, FDE)) { /* * When the full disk encryption (FDE) flag is set, * we map the first N sectors using a linear target * as they aren't encrypted. */ /* /dev/ad0s0a 0 */ /* dev---^ block off --^ */ snprintf(params, 512, "%s 0", dev); if ((dm_task_add_target(dmt, 0, info->offset, "linear", params)) == 0) { tc_log(1, "dm_task_add_target failed\n"); ret = -1; goto out; } start = info->offset; } /* aes-cbc-essiv:sha256 7997f8af... 0 /dev/ad0s0a 8 */ /* iv off---^ block off--^ */ snprintf(params, 512, "%s %s %"PRIu64 " %s %"PRIu64, cipher_chain->cipher->dm_crypt_str, cipher_chain->dm_key, (uint64_t)info->skip, dev, (uint64_t)offset); #ifdef DEBUG printf("Params: %s\n", params); #endif if ((dm_task_add_target(dmt, start, info->size, "crypt", params)) == 0) { tc_log(1, "dm_task_add_target failed\n"); ret = -1; goto out; } if ((dm_task_set_cookie(dmt, &cookie, 0)) == 0) { tc_log(1, "dm_task_set_cookie failed\n"); ret = -1; goto out; } if ((dm_task_run(dmt)) == 0) { dm_udev_wait(cookie); tc_log(1, "dm_task_run failed\n"); ret = -1; goto out; } if ((dm_task_get_info(dmt, &dmi)) == 0) { dm_udev_wait(cookie); tc_log(1, "dm_task_get info failed\n"); ret = -1; goto out; } dm_udev_wait(cookie); if ((r = asprintf(&uu_stack[uu_stack_idx++], "%s", map)) < 0) tc_log(1, "warning, asprintf failed. won't be able to " "unroll changes\n"); offset = 0; start = 0; sprintf(dev, "/dev/mapper/%s.%d", mapname, j); dm_task_destroy(dmt); dm_task_update_nodes(); } out: /* * If an error occured, try to unroll changes made before it * happened. */ if (ret) { j = uu_stack_idx; while (j > 0) { #ifdef DEBUG printf("Unrolling dm changes! j = %d (%s)\n", j-1, uu_stack[j-1]); #endif if ((uu_stack[j-1] == NULL) || ((r = dm_remove_device(uu_stack[--j])) != 0)) { tc_log(1, "Tried to unroll dm changes, " "giving up.\n"); break; } } } while (uu_stack_idx > 0) free(uu_stack[--uu_stack_idx]); free_safe_mem(params); return ret; } int dm_teardown(const char *mapname, const char *device __unused) { #if 0 struct dm_task *dmt = NULL; struct dm_info dmi; #endif char map[PATH_MAX]; int i, error; if ((error = dm_remove_device(mapname)) != 0) { tc_log(1, "Could not remove mapping %s\n", mapname); return error; } /* Try to remove other cascade devices */ for (i = 0; i < 2; i++) { sprintf(map, "%s.%d", mapname, i); if (dm_exists_device(map)) dm_remove_device(map); } return 0; } struct tc_crypto_algo * check_cipher(const char *cipher, int quiet) { int i, found = 0; for (i = 0; tc_crypto_algos[i].name != NULL; i++) { if (strcmp(cipher, tc_crypto_algos[i].name) == 0) { found = 1; break; } } if (!found && !quiet) { fprintf(stderr, "Valid ciphers are: "); for (i = 0; tc_crypto_algos[i].name != NULL; i++) fprintf(stderr, "%s ", tc_crypto_algos[i].name); fprintf(stderr, "\n"); return NULL; } return &tc_crypto_algos[i]; } struct tc_cipher_chain * check_cipher_chain(const char *cipher_chain, int quiet) { struct tc_cipher_chain *cipher = NULL; int i,k, nciphers = 0, mismatch = 0; char *ciphers[8]; char *tmp_chain, *tmp_chain_free; char *token; if ((tmp_chain = strdup(cipher_chain)) == NULL) { tc_log(1, "Could not allocate strdup memory\n"); return NULL; } tmp_chain_free = tmp_chain; while ((token = strsep(&tmp_chain, ",")) != NULL) ciphers[nciphers++] = token; cipher = NULL; for (i = 0; valid_cipher_chains[i][0] != NULL; i++) { mismatch = 0; for (k = 0; (valid_cipher_chains[i][k] != NULL); k++) { /* * If there are more ciphers in the chain than in the * ciphers[] variable this is not the right chain. */ if (k == nciphers) { mismatch = 1; break; } if (strcmp(ciphers[k], valid_cipher_chains[i][k]) != 0) mismatch = 1; } /* * If all ciphers matched and there are exactly nciphers, * then we found the right cipher chain. */ if ((k == nciphers) && !mismatch) { cipher = tc_cipher_chains[i]; break; } } if (cipher == NULL) { tc_log(1, "Invalid cipher: %s\n", cipher_chain); if (!quiet) { fprintf(stderr, "Valid cipher chains are:\n"); for (i = 0; valid_cipher_chains[i][0] != NULL; i++) { for (k = 0; valid_cipher_chains[i][k] != NULL; k++) { fprintf(stderr, "%s%c", valid_cipher_chains[i][k], (valid_cipher_chains[i][k+1] != NULL) ? ',' : '\0'); } fprintf(stderr, "\n"); } } } free(tmp_chain_free); return cipher; } struct pbkdf_prf_algo * check_prf_algo(const char *algo, int quiet) { int i, found = 0; for (i = 0; pbkdf_prf_algos[i].name != NULL; i++) { if (strcmp(algo, pbkdf_prf_algos[i].name) == 0) { found = 1; break; } } if (!found && !quiet) { fprintf(stderr, "Valid PBKDF PRF algorithms are: "); for (i = 0; pbkdf_prf_algos[i].name != NULL; i++) fprintf(stderr, "%s ", pbkdf_prf_algos[i].name); fprintf(stderr, "\n"); return NULL; } return &pbkdf_prf_algos[i]; } int tc_play_init(void) { int error; if ((error = tc_build_cipher_chains()) != 0) return error; if ((error = tc_crypto_init()) != 0) return error; return 0; } tc-play-1.1/tcplay.h000066400000000000000000000240701217612412400143740ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* Version of tcplay specified during build (CMakeLists.txt, Makefile.classic) */ #define MAX_BLKSZ 4096 #define MAX_KEYSZ 192 #define HDRSZ 512 #define HDR_OFFSET_SYS 31744 /* 512 * (63 -1) */ #define TC_SIG "TRUE" #define MAX_PASSSZ 64 #define PASS_BUFSZ 256 #define KPOOL_SZ 64 #define MAX_KFILE_SZ 1048576 /* 1 MB */ #define MAX_KEYFILES 256 #define HDR_OFFSET_HIDDEN 65536 #define BACKUP_HDR_HIDDEN_OFFSET_END 65536 #define BACKUP_HDR_OFFSET_END 131072 #define SALT_LEN 64 #define VOL_RSVD_BYTES_START (256*512) /* Reserved bytes at vol. start */ #define VOL_RSVD_BYTES_END (256*512) /* Reserved bytes at vol. end */ #define MIN_VOL_BYTES (VOL_RSVD_BYTES_START + VOL_RSVD_BYTES_END) #define MAX_CIPHER_CHAINS 64 #define DEFAULT_RETRIES 3 #define ERASE_BUFFER_SIZE 4*1024*1024 /* 4 MB */ /* TrueCrypt Volume flags */ #define TC_VOLFLAG_SYSTEM 0x01 /* system encryption */ #define TC_VOLFLAG_INPLACE 0x02 /* non-system in-place-encrypted volume */ #define TC_VOLFLAG_SET(f, x) ((f & TC_VOLFLAG_##x) == TC_VOLFLAG_##x) #define LOG_BUFFER_SZ 1024 #if 0 #define DEBUG 1 #endif #define TC_FLAG_SYS 0x0001 #define TC_FLAG_FDE 0x0002 #define TC_FLAG_BACKUP 0x0004 #define TC_FLAG_ONLY_RESTORE 0x0008 #define TC_FLAG_SET(f, x) ((f & TC_FLAG_##x) == TC_FLAG_##x) #include #include #if defined(__DragonFly__) #include #elif defined(__linux__) #include #endif struct pbkdf_prf_algo { const char *name; int iteration_count; }; struct tc_crypto_algo { const char *name; const char *dm_crypt_str; int klen; int ivlen; }; struct tc_cipher_chain { struct tc_crypto_algo *cipher; unsigned char *key; char dm_key[MAX_KEYSZ*2 + 1]; struct tc_cipher_chain *prev; struct tc_cipher_chain *next; }; struct tchdr_enc { unsigned char salt[SALT_LEN]; /* Salt for PBKDF */ unsigned char enc[448]; /* Encrypted part of the header */ } __attribute__((__packed__)); struct tchdr_dec { char tc_str[4]; /* ASCII string "TRUE" */ uint16_t tc_ver; /* Volume header format version */ uint16_t tc_min_ver; uint32_t crc_keys; /* CRC32 of the key section */ uint64_t vol_ctime; /* Volume creation time */ uint64_t hdr_ctime; /* Header creation time */ uint64_t sz_hidvol; /* Size of hidden volume (set to zero in non-hidden volumes) */ uint64_t sz_vol; /* Size of volume */ uint64_t off_mk_scope; /* Byte offset of the start of the master key scope */ uint64_t sz_mk_scope; /* Size of the encrypted area within the master key scope */ uint32_t flags; /* Flag bits (bit 0: system encryption; bit 1: non-system in-place-encrypted volume; bits 2–31 are reserved) */ uint32_t sec_sz; /* Sector size (in bytes) */ unsigned char unused3[120]; uint32_t crc_dhdr; /* CRC32 of dec. header (except keys) */ unsigned char keys[256]; } __attribute__((__packed__)); struct tcplay_info { char dev[PATH_MAX]; struct tchdr_dec *hdr; struct tc_cipher_chain *cipher_chain; struct pbkdf_prf_algo *pbkdf_prf; char key[MAX_KEYSZ*2 + 1]; int flags; int volflags; off_t start; /* Logical volume offset in table */ size_t size; /* Volume size */ off_t skip; /* IV offset */ off_t offset; /* Block offset */ /* Populated by dm_setup */ uuid_t uuid; int hidden; }; struct tcplay_dm_table { char device[PATH_MAX]; /* Underlying device */ char target[256]; /* DM Target type */ off_t start; /* Logical volume offset in table */ size_t size; /* Volume size */ char cipher[256]; /* Cipher */ off_t skip; /* IV offset */ off_t offset; /* Block offset */ }; void *read_to_safe_mem(const char *file, off_t offset, size_t *sz); int get_random(unsigned char *buf, size_t len, int weak); int secure_erase(const char *dev, size_t bytes, size_t blksz); int get_disk_info(const char *dev, size_t *blocks, size_t *bsize); int write_to_disk(const char *dev, off_t offset, size_t blksz, void *mem, size_t bytes); int read_passphrase(const char *prompt, char *pass, size_t passlen, size_t bufsz, time_t timeout); float get_random_read_progress(void); float get_secure_erase_progress(void); int tc_crypto_init(void); int tc_cipher_chain_populate_keys(struct tc_cipher_chain *cipher_chain, unsigned char *key); int tc_cipher_chain_free_keys(struct tc_cipher_chain *cipher_chain); int tc_encrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key, unsigned char *iv, unsigned char *in, int in_len, unsigned char *out); int tc_decrypt(struct tc_cipher_chain *cipher_chain, unsigned char *key, unsigned char *iv, unsigned char *in, int in_len, unsigned char *out); /* The following two are platform dependent */ int syscrypt(struct tc_crypto_algo *cipher, unsigned char *key, size_t klen, unsigned char *iv, unsigned char *in, unsigned char *out, size_t len, int do_encrypt); int pbkdf2(struct pbkdf_prf_algo *hash, const char *pass, int passlen, const unsigned char *salt, int saltlen, int keylen, unsigned char *out); int apply_keyfiles(unsigned char *pass, size_t pass_memsz, const char *keyfiles[], int nkeyfiles); struct tchdr_enc *create_hdr(unsigned char *pass, int passlen, struct pbkdf_prf_algo *prf_algo, struct tc_cipher_chain *cipher_chain, size_t sec_sz, size_t total_blocks, off_t offset, size_t blocks, int hidden, int weak, struct tchdr_enc **backup_hdr); struct tchdr_dec *decrypt_hdr(struct tchdr_enc *ehdr, struct tc_cipher_chain *cipher_chain, unsigned char *key); int verify_hdr(struct tchdr_dec *hdr); struct tchdr_enc *copy_reencrypt_hdr(unsigned char *pass, int passlen, struct pbkdf_prf_algo *prf_algo, int weak, struct tcplay_info *info, struct tchdr_enc **backup_hdr); void *_alloc_safe_mem(size_t req_sz, const char *file, int line); void _free_safe_mem(void *mem, const char *file, int line); void check_and_purge_safe_mem(void); struct tc_crypto_algo *check_cipher(const char *cipher, int quiet); struct tc_cipher_chain *check_cipher_chain(const char *cipher_chain, int quiet); struct pbkdf_prf_algo *check_prf_algo(const char *algo, int quiet); int tc_play_init(void); void tc_log(int err, const char *fmt, ...); int tc_cipher_chain_klen(struct tc_cipher_chain *chain); char *tc_cipher_chain_sprint(char *buf, size_t bufsz, struct tc_cipher_chain *chain); void print_info(struct tcplay_info *info); int adjust_info(struct tcplay_info *info, struct tcplay_info *hinfo); int process_hdr(const char *dev, int flags, unsigned char *pass, int passlen, struct tchdr_enc *ehdr, struct tcplay_info **pinfo); int create_volume(const char *dev, int hidden, const char *keyfiles[], int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles, struct pbkdf_prf_algo *prf_algo, struct tc_cipher_chain *cipher_chain, struct pbkdf_prf_algo *h_prf_algo, struct tc_cipher_chain *h_cipher_chain, const char *passphrase, const char *h_passphrase, size_t hidden_bytes_in, int interactive, int secure_erase, int weak_keys); struct tcplay_info *info_map_common(const char *dev, int flags, const char *sys_dev, int protect_hidden, const char *keyfiles[], int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles, const char *passphrase, const char *passphrase_hidden, int interactive, int retries, time_t timeout, char *passphrase_out); int info_mapped_volume(const char *map_name, int interactive); int info_volume(const char *device, int flags, const char *sys_dev, int protect_hidden, const char *keyfiles[], int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles, const char *passphrase, const char *passphrase_hidden, int interactive, int retries, time_t timeout); int map_volume(const char *map_name, const char *device, int flags, const char *sys_dev, int protect_hidden, const char *keyfiles[], int nkeyfiles, const char *h_keyfiles[], int n_hkeyfiles, const char *passphrase, const char *passphrase_hidden, int interactive, int retries, time_t timeout); int modify_volume(const char *device, int flags, const char *sys_dev, const char *keyfiles[], int nkeyfiles, const char *new_keyfiles[], int n_newkeyfiles, struct pbkdf_prf_algo *new_prf_algo, const char *passphrase, const char *new_passphrase, int interactive, int retries, time_t timeout, int weak_salt); int dm_setup(const char *mapname, struct tcplay_info *info); int dm_teardown(const char *mapname, const char *device); struct tcplay_info *dm_info_map(const char *map_name); typedef void(*summary_fn_t)(void); extern int tc_internal_verbose; extern char tc_internal_log_buffer[]; extern summary_fn_t summary_fn; #define STATE_UNKNOWN 0 #define STATE_GET_RANDOM 1 #define STATE_ERASE 2 extern int tc_internal_state; #define alloc_safe_mem(x) \ _alloc_safe_mem(x, __FILE__, __LINE__) #define free_safe_mem(x) \ _free_safe_mem(x, __FILE__, __LINE__) #define __unused __attribute__((__unused__)) tc-play-1.1/tcplay.map000066400000000000000000000004541217612412400147220ustar00rootroot00000000000000{ global: tc_api_init; tc_api_uninit; tc_api_create_volume; tc_api_map_volume; tc_api_unmap_volume; tc_api_check_cipher; tc_api_check_prf_hash; tc_api_get_error_msg; tc_api_get_summary; tc_api_get_state; tc_api_info_volume; tc_api_info_mapped_volume; tc_api_modify_volume; local: *; }; tc-play-1.1/tcplay_api.c000066400000000000000000000236411217612412400152230ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include "tcplay.h" #include "tcplay_api.h" int tc_api_init(int verbose) { int error; tc_internal_verbose = verbose; if ((error = tc_play_init()) != 0) return TC_ERR; else return TC_OK; } int tc_api_uninit(void) { check_and_purge_safe_mem(); return TC_OK; } const char * tc_api_get_error_msg(void) { return tc_internal_log_buffer; } const char * tc_api_get_summary(void) { if (summary_fn != NULL) { summary_fn(); return tc_internal_log_buffer; } return NULL; } tc_api_state tc_api_get_state(float *progress) { switch (tc_internal_state) { case STATE_UNKNOWN: return TC_STATE_UNKNOWN; case STATE_ERASE: if (progress != NULL) *progress = get_secure_erase_progress(); return TC_STATE_ERASE; case STATE_GET_RANDOM: if (progress != NULL) *progress = get_random_read_progress(); return TC_STATE_GET_RANDOM; default: return TC_STATE_UNKNOWN; } } int tc_api_create_volume(tc_api_opts *api_opts) { int nkeyfiles, n_hkeyfiles = 0; int create_hidden; int err; if ((api_opts == NULL) || (api_opts->tc_device == NULL)) { errno = EFAULT; return TC_ERR; } if ((err = tc_api_check_cipher(api_opts)) != TC_OK) return TC_ERR; if ((err = tc_api_check_prf_hash(api_opts)) != TC_OK) return TC_ERR; for (nkeyfiles = 0; (nkeyfiles < MAX_KEYFILES) && (api_opts->tc_keyfiles != NULL) && (api_opts->tc_keyfiles[nkeyfiles] != NULL); nkeyfiles++) ; create_hidden = 0; if (api_opts->tc_size_hidden_in_bytes > 0) { create_hidden = 1; for (n_hkeyfiles = 0; (n_hkeyfiles < MAX_KEYFILES) && (api_opts->tc_keyfiles_hidden != NULL) && (api_opts->tc_keyfiles_hidden[n_hkeyfiles] != NULL); n_hkeyfiles++) ; } err = create_volume(api_opts->tc_device, create_hidden, api_opts->tc_keyfiles, nkeyfiles, api_opts->tc_keyfiles_hidden, n_hkeyfiles, check_prf_algo(api_opts->tc_prf_hash, 1), check_cipher_chain(api_opts->tc_cipher, 1), api_opts->tc_prf_hash_hidden ? check_prf_algo(api_opts->tc_prf_hash_hidden, 1) : NULL, api_opts->tc_cipher_hidden ? check_cipher_chain(api_opts->tc_cipher_hidden, 1) : NULL, api_opts->tc_passphrase, api_opts->tc_passphrase_hidden, api_opts->tc_size_hidden_in_bytes, 0 /* non-interactive */, !api_opts->tc_no_secure_erase, api_opts->tc_use_weak_keys); return (err) ? TC_ERR : TC_OK; } int tc_api_map_volume(tc_api_opts *api_opts) { int nkeyfiles, n_hkeyfiles = 0; int err; int flags = 0; if ((api_opts == NULL) || (api_opts->tc_map_name == NULL) || (api_opts->tc_device == NULL)) { errno = EFAULT; return TC_ERR; } for (nkeyfiles = 0; (nkeyfiles < MAX_KEYFILES) && (api_opts->tc_keyfiles != NULL) && (api_opts->tc_keyfiles[nkeyfiles] != NULL); nkeyfiles++) ; if (api_opts->tc_protect_hidden) { for (n_hkeyfiles = 0; (n_hkeyfiles < MAX_KEYFILES) && (api_opts->tc_keyfiles_hidden != NULL) && (api_opts->tc_keyfiles_hidden[n_hkeyfiles] != NULL); n_hkeyfiles++) ; } if (api_opts->tc_use_system_encryption) flags |= TC_FLAG_SYS; if (api_opts->tc_use_fde) flags |= TC_FLAG_FDE; if (api_opts->tc_use_backup) flags |= TC_FLAG_BACKUP; err = map_volume(api_opts->tc_map_name, api_opts->tc_device, flags, api_opts->tc_system_device, api_opts->tc_protect_hidden, api_opts->tc_keyfiles, nkeyfiles, api_opts->tc_keyfiles_hidden, n_hkeyfiles, api_opts->tc_passphrase, api_opts->tc_passphrase_hidden, api_opts->tc_interactive_prompt, api_opts->tc_password_retries, (time_t)api_opts->tc_prompt_timeout); return (err) ? TC_ERR : TC_OK; } int tc_api_info_volume(tc_api_opts *api_opts, tc_api_volinfo *vol_info) { struct tcplay_info *info; int nkeyfiles, n_hkeyfiles = 0; int flags = 0; if ((api_opts == NULL) || (vol_info == NULL) || (api_opts->tc_device == NULL)) { errno = EFAULT; return TC_ERR; } for (nkeyfiles = 0; (nkeyfiles < MAX_KEYFILES) && (api_opts->tc_keyfiles != NULL) && (api_opts->tc_keyfiles[nkeyfiles] != NULL); nkeyfiles++) ; if (api_opts->tc_protect_hidden) { for (n_hkeyfiles = 0; (n_hkeyfiles < MAX_KEYFILES) && (api_opts->tc_keyfiles_hidden != NULL) && (api_opts->tc_keyfiles_hidden[n_hkeyfiles] != NULL); n_hkeyfiles++) ; } if (api_opts->tc_use_system_encryption) flags |= TC_FLAG_SYS; if (api_opts->tc_use_fde) flags |= TC_FLAG_FDE; if (api_opts->tc_use_backup) flags |= TC_FLAG_BACKUP; info = info_map_common(api_opts->tc_device, flags, api_opts->tc_system_device, api_opts->tc_protect_hidden, api_opts->tc_keyfiles, nkeyfiles, api_opts->tc_keyfiles_hidden, n_hkeyfiles, api_opts->tc_passphrase, api_opts->tc_passphrase_hidden, api_opts->tc_interactive_prompt, api_opts->tc_password_retries, (time_t)api_opts->tc_prompt_timeout, NULL); if (info == NULL || info->hdr == NULL) return TC_ERR; tc_cipher_chain_sprint(vol_info->tc_cipher, sizeof(vol_info->tc_cipher), info->cipher_chain); vol_info->tc_key_bits = 8*tc_cipher_chain_klen(info->cipher_chain); strncpy(vol_info->tc_prf, info->pbkdf_prf->name, sizeof(vol_info->tc_prf)); vol_info->tc_size = info->size * (off_t)info->hdr->sec_sz; vol_info->tc_iv_offset = info->skip * (off_t)info->hdr->sec_sz; vol_info->tc_block_offset = info->offset * (off_t)info->hdr->sec_sz; strncpy(vol_info->tc_device, info->dev, sizeof(vol_info->tc_device)); vol_info->tc_device[sizeof(vol_info->tc_device)-1] = '\0'; free_safe_mem(info->hdr); free_safe_mem(info); return TC_OK; } int tc_api_info_mapped_volume(tc_api_opts *api_opts, tc_api_volinfo *vol_info) { struct tcplay_info *info; if ((api_opts == NULL) || (vol_info == NULL) || (api_opts->tc_map_name == NULL)) { errno = EFAULT; return TC_ERR; } info = dm_info_map(api_opts->tc_map_name); if (info == NULL) return TC_ERR; tc_cipher_chain_sprint(vol_info->tc_cipher, sizeof(vol_info->tc_cipher), info->cipher_chain); vol_info->tc_key_bits = 8*tc_cipher_chain_klen(info->cipher_chain); strncpy(vol_info->tc_prf, "(unknown)", sizeof(vol_info->tc_prf)); vol_info->tc_size = info->size * (size_t)512; vol_info->tc_iv_offset = info->skip * (off_t)512; vol_info->tc_block_offset = info->offset * (off_t)512; strncpy(vol_info->tc_device, info->dev, sizeof(vol_info->tc_device)); vol_info->tc_device[sizeof(vol_info->tc_device)-1] = '\0'; free_safe_mem(info); return TC_OK; } int tc_api_modify_volume(tc_api_opts *api_opts) { struct pbkdf_prf_algo *prf_hash = NULL; int nkeyfiles, n_newkeyfiles = 0; int flags = 0; int error; if ((api_opts == NULL) || (api_opts->tc_device == NULL)) { errno = EFAULT; return TC_ERR; } if (api_opts->tc_new_prf_hash != NULL) { if ((prf_hash = check_prf_algo(api_opts->tc_new_prf_hash, 1)) == NULL) { errno = EINVAL; return TC_ERR; } } for (nkeyfiles = 0; (nkeyfiles < MAX_KEYFILES) && (api_opts->tc_keyfiles != NULL) && (api_opts->tc_keyfiles[nkeyfiles] != NULL); nkeyfiles++) ; for (n_newkeyfiles = 0; (n_newkeyfiles < MAX_KEYFILES) && (api_opts->tc_new_keyfiles != NULL) && (api_opts->tc_new_keyfiles[n_newkeyfiles] != NULL); n_newkeyfiles++) ; if (api_opts->tc_use_system_encryption) flags |= TC_FLAG_SYS; if (api_opts->tc_use_fde) flags |= TC_FLAG_FDE; if (api_opts->tc_use_backup) flags |= TC_FLAG_BACKUP; error = modify_volume(api_opts->tc_device, flags, api_opts->tc_system_device, api_opts->tc_keyfiles, nkeyfiles, api_opts->tc_new_keyfiles, n_newkeyfiles, prf_hash, api_opts->tc_passphrase, api_opts->tc_new_passphrase, api_opts->tc_interactive_prompt, api_opts->tc_password_retries, (time_t)api_opts->tc_prompt_timeout, api_opts->tc_use_weak_salt); if (error) return TC_ERR; return TC_OK; } int tc_api_unmap_volume(tc_api_opts *api_opts) { int err; if ((api_opts == NULL) || (api_opts->tc_map_name == NULL)) { errno = EFAULT; return TC_ERR; } err = dm_teardown(api_opts->tc_map_name, api_opts->tc_device); return (err) ? TC_ERR : TC_OK; } int tc_api_check_cipher(tc_api_opts *api_opts) { struct tc_cipher_chain *chain; if (api_opts == NULL || api_opts->tc_cipher == NULL) { errno = EFAULT; return TC_ERR; } if ((chain = check_cipher_chain(api_opts->tc_cipher, 1)) != NULL) return TC_OK; errno = ENOENT; return TC_ERR; } int tc_api_check_prf_hash(tc_api_opts *api_opts) { struct pbkdf_prf_algo *prf_hash; if (api_opts == NULL || api_opts->tc_prf_hash == NULL) { errno = EFAULT; return TC_ERR; } if ((prf_hash = check_prf_algo(api_opts->tc_prf_hash, 1)) != NULL) return TC_OK; errno = ENOENT; return TC_ERR; } tc-play-1.1/tcplay_api.h000066400000000000000000000065411217612412400152300ustar00rootroot00000000000000/* * Copyright (c) 2011 Alex Hornung . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _TCPLAY_API_H #define _TCPLAY_API_H #include #define TC_OK 0 #define TC_ERR -1 typedef enum tc_api_state { TC_STATE_UNKNOWN, TC_STATE_ERASE, TC_STATE_GET_RANDOM } tc_api_state; typedef struct tc_api_opts { /* Common fields */ const char *tc_device; const char *tc_passphrase; const char **tc_keyfiles; const char *tc_passphrase_hidden; const char **tc_keyfiles_hidden; /* Fields for mapping / info */ const char *tc_map_name; int tc_protect_hidden; /* Fields for mapping / info / modify */ int tc_password_retries; int tc_interactive_prompt; unsigned long tc_prompt_timeout; int tc_use_system_encryption; const char *tc_system_device; int tc_use_fde; int tc_use_backup; /* Fields for modify */ const char *tc_new_passphrase; const char **tc_new_keyfiles; const char *tc_new_prf_hash; int tc_use_weak_salt; /* Fields for creation */ const char *tc_cipher; const char *tc_prf_hash; const char *tc_cipher_hidden; const char *tc_prf_hash_hidden; size_t tc_size_hidden_in_bytes; int tc_no_secure_erase; int tc_use_weak_keys; } tc_api_opts; typedef struct tc_api_volinfo { char tc_device[1024]; char tc_cipher[256]; char tc_prf[64]; int tc_key_bits; size_t tc_size; off_t tc_iv_offset; off_t tc_block_offset; } tc_api_volinfo; #ifdef __cplusplus extern "C" { #endif int tc_api_init(int verbose); int tc_api_uninit(void); int tc_api_info_volume(tc_api_opts *api_opts, tc_api_volinfo *vol_info); int tc_api_info_mapped_volume(tc_api_opts *api_opts, tc_api_volinfo *vol_info); int tc_api_create_volume(tc_api_opts *api_opts); int tc_api_modify_volume(tc_api_opts *api_opts); int tc_api_map_volume(tc_api_opts *api_opts); int tc_api_unmap_volume(tc_api_opts *api_opts); int tc_api_check_cipher(tc_api_opts *api_opts); int tc_api_check_prf_hash(tc_api_opts *api_opts); const char *tc_api_get_error_msg(void); const char *tc_api_get_summary(void); tc_api_state tc_api_get_state(float *progress_pct); #ifdef __cplusplus } #endif #endif tc-play-1.1/tcplay_api_test.c000066400000000000000000000025351217612412400162610ustar00rootroot00000000000000#include #include #include #include #include "tcplay_api.h" int main(void) { tc_api_opts api_opts; int error; error = tc_api_init(/* verbose */1); assert(error == 0); memset(&api_opts, 0, sizeof(api_opts)); api_opts.tc_device = "/dev/vn1s0"; api_opts.tc_passphrase = "apitest2"; api_opts.tc_keyfiles = NULL; api_opts.tc_keyfiles_hidden = NULL; api_opts.tc_size_hidden_in_bytes = 12000*512; api_opts.tc_passphrase_hidden = "apihidden"; api_opts.tc_cipher = "AES-256-XTS,TWOFISH-256-XTS,SERPENT-256-XTS"; api_opts.tc_cipher_hidden = "SERPENT-256-XTS,TWOFISH-256-XTS"; api_opts.tc_prf_hash = "whirlpool"; api_opts.tc_prf_hash_hidden = "RIPEMD160"; error = tc_api_create_volume(&api_opts); if (error) printf("API ERROR: %s\n", tc_api_get_error_msg()); assert(error == 0); error = tc_api_uninit(); assert(error == 0); error = tc_api_init(/*verbose */ 1); assert(error == 0); api_opts.tc_passphrase = NULL; api_opts.tc_map_name = "dragonfly-test"; api_opts.tc_password_retries = 5; api_opts.tc_interactive_prompt = 1; api_opts.tc_prompt_timeout = 5; error = tc_api_map_volume(&api_opts); if (error) printf("API MAP ERROR: %s\n", tc_api_get_error_msg()); assert(error == 0); system("dmsetup ls"); tc_api_unmap_volume(&api_opts); error = tc_api_uninit(); assert(error == 0); return 0; } tc-play-1.1/test/000077500000000000000000000000001217612412400137035ustar00rootroot00000000000000tc-play-1.1/test/Gemfile000066400000000000000000000002131217612412400151720ustar00rootroot00000000000000source "http://rubygems.org" gem "json", "~> 1.7.7" gem "cucumber", "~> 1.3.1" gem "rspec" , "~> 2.13.0" gem "ffi" , "~> 1.8.1" tc-play-1.1/test/Makefile000066400000000000000000000000751217612412400153450ustar00rootroot00000000000000test: sudo bundle exec cucumber bootstrap: bundle install tc-play-1.1/test/README000066400000000000000000000141201217612412400145610ustar00rootroot00000000000000Introduction ============ The tcplay tests use cucumber as test driver. Test cases are known as Scenarios and are grouped by Feature. The .feature files in features/ contain the Scenarios. What each step in a scenario does is defined in the step_definitions files. Running the tests ============ To run the tests, you first need to bootstrap the environment. Running the following will install all required ruby gems: make bootstrap You only have to do that once. After that, you can run tests using: make test The tests must run as root, so sudo will prompt you for your password before running the actual tests. To be able to run the tests, you need to have 'libtcplay.so' and 'tcplay' in the source directory. To get them, just change to the source directory and run make -f Makefile.classic clean all Steps ============ The tcplay tests have the following step definitions, some of which use the command line tool and some of which use the library (API): * "Given I map volume as using the following settings:" * "Given I map volume as with the API using the following settings:" Where is a file name containing a volume and is the name of the mapped device. The settings are specified in a table - the following are all possible settings: passphrase | Any passphrase keyfiles | path_to_kf1, path_to_kf2, ... protect_hidden | yes/no passphrase_hidden | Any passphrase keyfiles_hidden | path_to_kf1, path_to_kf2, ... use_backup | yes/no * "Given I request information about volume using the following settings:" * "Given I request information about volume with the API using the following settings:" Where is a file name containing a volume. The settings are specified in a table - the following are all possible settings: passphrase | Any passphrase keyfiles | path_to_kf1, path_to_kf2, ... protect_hidden | yes/no passphrase_hidden | Any passphrase keyfiles_hidden | path_to_kf1, path_to_kf2, ... use_backup | yes/no * "Given I request information about mapped volume " * "Given I request information about mapped volume with the API" Where is the mapped name of a volume previously mapped. * "Given I create a volume of size M with the following parameters:" * "Given I create a volume of size M using the API with the following parameters:" Where is a file name that will be created to contain the newly created volume. Its size will be MB. The parameters are specified in a table - the following are all possible parameters: passphrase | Any passphrase keyfiles | path_to_kf1, path_to_kf2, ... pbkdf_prf | Any valid tcplay PBKDF PRF cipher | Any valid tcplay cipher chain create_hidden | yes/no hidden_mb | A numeric size, e.g. 5.5 (meaning 5.5 MB) passphrase_hidden | Any passphrase keyfiles_hidden | path_to_kf1, path_to_kf2, ... pbkdf_prf_hidden | Any valid tcplay PBKDF PRF cipher_hidden | Any valid tcplay cipher chain * "Given I modify volume using the following settings:" * "Given I modify volume with the API using the following settings:" Where is a file name containing a volume. The settings are specified in a table - the following are all possible settings: passphrase | Any passphrase keyfiles | path_to_kf1, path_to_kf2, ... new_passphrase | Any passphrase new_keyfiles | path_to_kf1, path_to_kf2, ... new_pbkdf_prf | Any valid tcplay PBKDF PRF use_backup | yes/no * "Given I modify volume by restoring from the backup header using the following settings:" Where is a file name containing a volume. The settings are specified in a table - the following are all possible settings: passphrase | Any passphrase keyfiles | path_to_kf1, path_to_kf2, ... * "Then I expect dmsetup to have the following tables:" The tables that should have been created by a previous "Given I map..." are specified as a table with the following headers: name | Name of the mapped volume begin | First sector of the table end | Last sector of the table algo | Algorithm used for dm-crypt key | The master key, in hexadecimal offset | The offset on the raw device at which the volume starts iv_offset | The IV offset * "Then I expect tcplay to report the following:" The information tcplay should report after a previous "Given I request information" is specified as a table with the following headers (which are the same as the keys output by the --info command to tcplay): PBKDF2 PRF | Any valid tcplay PBKDF PRF PBKDF2 iterations | A numeric value Cipher | Any valid tcplay cipher chain Key Length | A string such as "512 bits" CRC Key Data | A hexadecimal value such as "0xdeadc0de" Sector size | A numeric value such as 512 Volume size | A string such as "40448 sectors" IV offset | A numeric value (in blocks) Block offset | A numeric value (in blocks) The output of the API information request has the following fields instead: PBKDF2 PRF | Any valid tcplay PBKDF PRF Cipher | Any valid tcplay cipher chain Key Length | A string such as "512 bits" Volume size | A string such as "20709376 bytes" IV offset | A string such as "131072 bytes" Block offset | A string such as "131072 bytes" The "Given I request information about mapped volume" steps will generate the same information, except the PBKDF2 and CRC Key Data fields will be missing. * "Then I expect tcplay to succeed" * "Then I expect tcplay to fail" tc-play-1.1/test/features/000077500000000000000000000000001217612412400155215ustar00rootroot00000000000000tc-play-1.1/test/features/create_vol_api.feature000066400000000000000000000141201217612412400220500ustar00rootroot00000000000000@api Feature: API volume creation Scenario: Simple volume #1 Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | SHA512 | | cipher | AES-256-XTS | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | test | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | test | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | SHA512 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 10223616 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Cascade volume #1 Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | AES-256-XTS,SERPENT-256-XTS | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | test | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | test | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test.0 | 0 | 19968 | serpent-xts-plain64 | 256 | 256 | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 0 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | Cipher | AES-256-XTS,SERPENT-256-XTS | | Key Length | 1024 bits | | Volume size | 10223616 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Hidden volume #1 Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | test | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | twofish-xts-plain64 | 256 | 256 | Scenario: Hidden volume #2 Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | keyfiles | key.2 | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 4096 | serpent-xts-plain64 | 16128 | 16128 | Scenario: Hidden volume #3 Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | passphrase_hidden | hiddenhidden | | keyfiles_hidden | key.2, key.1 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | hiddenhidden | | keyfiles | key.1, key.2 | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | hiddenhidden | | keyfiles | key.1, key.2 | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 4096 | serpent-xts-plain64 | 16128 | 16128 | And I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | Cipher | SERPENT-256-XTS | | Key Length | 512 bits | | Volume size | 2097152 bytes | | IV offset | 8257536 bytes | | Block offset | 8257536 bytes | tc-play-1.1/test/features/create_vol_cli.feature000066400000000000000000000170331217612412400220540ustar00rootroot00000000000000@cmdline Feature: Command line volume creation Scenario: Simple volume #1 Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | SHA512 | | cipher | AES-256-XTS | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | test | And I request information about volume tmpvol1 using the following settings: | passphrase | test | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | SHA512 | | PBKDF2 iterations | 1000 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 19968 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Cascade volume #1 Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | AES-256-XTS,SERPENT-256-XTS | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | test | And I request information about volume tmpvol1 using the following settings: | passphrase | test | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test.0 | 0 | 19968 | serpent-xts-plain64 | 256 | 256 | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 0 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | PBKDF2 iterations | 1000 | | Cipher | AES-256-XTS,SERPENT-256-XTS | | Key Length | 1024 bits | | Sector size | 512 | | Volume size | 19968 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Hidden volume #1 Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | test | And I request information about volume tmpvol1 using the following settings: | passphrase | test | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | twofish-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | PBKDF2 iterations | 1000 | | Cipher | TWOFISH-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 19968 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Hidden volume #2 Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I map volume tmpvol1 as tcplay_test using the following settings: | keyfiles | key.2 | And I request information about volume tmpvol1 using the following settings: | keyfiles | key.2 | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 4096 | serpent-xts-plain64 | 16128 | 16128 | And I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | PBKDF2 iterations | 2000 | | Cipher | SERPENT-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 4096 sectors | | IV offset | 16128 | | Block offset | 16128 | Scenario: Hidden volume #3 Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | passphrase_hidden | hiddenhidden | | keyfiles_hidden | key.2, key.1 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | hiddenhidden | | keyfiles | key.1, key.2 | And I request information about volume tmpvol1 using the following settings: | passphrase | hiddenhidden | | keyfiles | key.1, key.2 | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 4096 | serpent-xts-plain64 | 16128 | 16128 | And I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | PBKDF2 iterations | 2000 | | Cipher | SERPENT-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 4096 sectors | | IV offset | 16128 | | Block offset | 16128 | tc-play-1.1/test/features/modify_vol_api.feature000066400000000000000000000242541217612412400221050ustar00rootroot00000000000000@api Feature: API volume modification Scenario: Simple volume #1 (change passphrase, neg) Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | SHA512 | | cipher | AES-256-XTS | And I modify volume tmpvol1 with the API using the following settings: | passphrase | test | | new_passphrase | new | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | test | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | test | Then I expect tcplay to fail Scenario: Simple volume #1 (change passphrase, pos) Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | SHA512 | | cipher | AES-256-XTS | And I modify volume tmpvol1 with the API using the following settings: | passphrase | test | | new_passphrase | new | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | new | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | new | Then I expect tcplay to succeed And I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | SHA512 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 10223616 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Simple volume #1 (change PBKDF PRF) Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | SHA512 | | cipher | AES-256-XTS | And I modify volume tmpvol1 with the API using the following settings: | passphrase | test | | new_passphrase | test | | new_pbkdf_prf | whirlpool | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | test | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | test | Then I expect tcplay to succeed And I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 10223616 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Cascade volume #1 (change passphrase) Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | AES-256-XTS,SERPENT-256-XTS | And I modify volume tmpvol1 with the API using the following settings: | passphrase | test | | new_passphrase | new | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | new | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | new | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test.0 | 0 | 19968 | serpent-xts-plain64 | 256 | 256 | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 0 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | Cipher | AES-256-XTS,SERPENT-256-XTS | | Key Length | 1024 bits | | Volume size | 10223616 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Hidden volume #1 (change passphrase) Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I modify volume tmpvol1 with the API using the following settings: | keyfiles | key.2 | | new_passphrase | new_hidden | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | test | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | test | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | twofish-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | Cipher | TWOFISH-256-XTS | | Key Length | 512 bits | | Volume size | 10223616 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Hidden volume #2 (change passphrase, keyfiles) Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I modify volume tmpvol1 with the API using the following settings: | keyfiles | key.2 | | new_passphrase | new_hidden | | new_keyfiles | key.1, key.2 | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | new_hidden | | keyfiles | key.1, key.2 | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | new_hidden | | keyfiles | key.1, key.2 | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 4096 | serpent-xts-plain64 | 16128 | 16128 | And I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | Cipher | SERPENT-256-XTS | | Key Length | 512 bits | | Volume size | 2097152 bytes | | IV offset | 8257536 bytes | | Block offset | 8257536 bytes | Scenario: Hidden volume #3 (change keyfiles, PBKDF PRF) Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I modify volume tmpvol1 with the API using the following settings: | keyfiles | key.2 | | new_keyfiles | key.1, key.2 | | new_pbkdf_prf | SHA512 | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | keyfiles | key.1, key.2 | And I request information about volume tmpvol1 with the API using the following settings: | keyfiles | key.1, key.2 | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 4096 | serpent-xts-plain64 | 16128 | 16128 | And I expect tcplay to report the following: | PBKDF2 PRF | SHA512 | | Cipher | SERPENT-256-XTS | | Key Length | 512 bits | | Volume size | 2097152 bytes | | IV offset | 8257536 bytes | | Block offset | 8257536 bytes | tc-play-1.1/test/features/modify_vol_cli.feature000066400000000000000000000326321217612412400221020ustar00rootroot00000000000000@cmdline Feature: Command line volume modification Scenario: Simple volume #1 (change passphrase, pos) Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | SHA512 | | cipher | AES-256-XTS | And I modify volume tmpvol1 using the following settings: | passphrase | test | | new_passphrase | new | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | new | And I request information about volume tmpvol1 using the following settings: | passphrase | new | Then I expect tcplay to succeed And I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | SHA512 | | PBKDF2 iterations | 1000 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 19968 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Simple volume #1 (change PBKDF PRF) Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | SHA512 | | cipher | AES-256-XTS | And I modify volume tmpvol1 using the following settings: | passphrase | test | | new_passphrase | test | | new_pbkdf_prf | whirlpool | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | test | And I request information about volume tmpvol1 using the following settings: | passphrase | test | Then I expect tcplay to succeed And I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | PBKDF2 iterations | 1000 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 19968 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Cascade volume #1 (change passphrase) Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | AES-256-XTS,SERPENT-256-XTS | And I modify volume tmpvol1 using the following settings: | passphrase | test | | new_passphrase | new | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | new | And I request information about volume tmpvol1 using the following settings: | passphrase | new | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test.0 | 0 | 19968 | serpent-xts-plain64 | 256 | 256 | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 0 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | PBKDF2 iterations | 1000 | | Cipher | AES-256-XTS,SERPENT-256-XTS | | Key Length | 1024 bits | | Sector size | 512 | | Volume size | 19968 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Hidden volume #1 (change passphrase) Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I modify volume tmpvol1 using the following settings: | keyfiles | key.2 | | new_passphrase | new_hidden | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | test | And I request information about volume tmpvol1 using the following settings: | passphrase | test | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | twofish-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | PBKDF2 iterations | 1000 | | Cipher | TWOFISH-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 19968 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Hidden volume #2 (change passphrase, keyfiles) Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I modify volume tmpvol1 using the following settings: | keyfiles | key.2 | | new_passphrase | new_hidden | | new_keyfiles | key.1, key.2 | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | new_hidden | | keyfiles | key.1, key.2 | And I request information about volume tmpvol1 using the following settings: | passphrase | new_hidden | | keyfiles | key.1, key.2 | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 4096 | serpent-xts-plain64 | 16128 | 16128 | And I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | PBKDF2 iterations | 2000 | | Cipher | SERPENT-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 4096 sectors | | IV offset | 16128 | | Block offset | 16128 | Scenario: Hidden volume #3 (change keyfiles, PBKDF PRF) Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I modify volume tmpvol1 using the following settings: | keyfiles | key.2 | | new_keyfiles | key.1, key.2 | | new_pbkdf_prf | SHA512 | And I map volume tmpvol1 as tcplay_test using the following settings: | keyfiles | key.1, key.2 | And I request information about volume tmpvol1 using the following settings: | keyfiles | key.1, key.2 | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 4096 | serpent-xts-plain64 | 16128 | 16128 | And I expect tcplay to report the following: | PBKDF2 PRF | SHA512 | | PBKDF2 iterations | 1000 | | Cipher | SERPENT-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 4096 sectors | | IV offset | 16128 | | Block offset | 16128 | Scenario: Hidden volume #4 (restore from backup, outer) Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I corrupt sector 0 of volume tmpvol1 And I modify volume tmpvol1 by restoring from the backup header using the following settings: | passphrase | test | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | test | And I request information about volume tmpvol1 using the following settings: | passphrase | test | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | twofish-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | PBKDF2 iterations | 1000 | | Cipher | TWOFISH-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 19968 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Hidden volume #5 (restore from backup, hidden) Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | test | | pbkdf_prf | whirlpool | | cipher | TWOFISH-256-XTS | | create_hidden | yes | | hidden_mb | 2 | | keyfiles_hidden | key.2 | | pbkdf_prf_hidden | RIPEMD160 | | cipher_hidden | SERPENT-256-XTS | And I corrupt sector 128 of volume tmpvol1 And I modify volume tmpvol1 by restoring from the backup header using the following settings: | keyfiles | key.2 | And I map volume tmpvol1 as tcplay_test using the following settings: | keyfiles | key.2 | And I request information about volume tmpvol1 using the following settings: | keyfiles | key.2 | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 4096 | serpent-xts-plain64 | 16128 | 16128 | And I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | PBKDF2 iterations | 2000 | | Cipher | SERPENT-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 4096 sectors | | IV offset | 16128 | | Block offset | 16128 | tc-play-1.1/test/features/passphrase_64_api.feature000066400000000000000000000072301217612412400224130ustar00rootroot00000000000000@api Feature: Passphrase 64-byte limitation using API Scenario: Long passphrase TrueCrypt-created volume mapping Given I map volume test_long.tc as tcplay_test with the API using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 1536 | aes-xts-plain64 | 256 | 256 | Scenario: Long passphrase TrueCrypt-created volume info Given I request information about volume test_long.tc with the API using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest | Then I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 786432 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Long passphrase TrueCrypt-created volume mapping with trimming Given I map volume test_long.tc as tcplay_test with the API using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttestexceedslimitation | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 1536 | aes-xts-plain64 | 256 | 256 | Scenario: Long passphrase TrueCrypt-created volume info with trimming Given I request information about volume test_long.tc with the API using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttestexceedslimitation | Then I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 786432 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Long passphrase volume creation Given I create a volume tmpvol1 of size 10M using the API with the following parameters: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttestexceeds64chars | | pbkdf_prf | SHA512 | | cipher | AES-256-XTS | And I map volume tmpvol1 as tcplay_test with the API using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest | And I request information about volume tmpvol1 with the API using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | SHA512 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 10223616 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | tc-play-1.1/test/features/passphrase_64_cli.feature000066400000000000000000000075571217612412400224250ustar00rootroot00000000000000@cmdline Feature: Passphrase 64-byte limitation using command line Scenario: Long passphrase TrueCrypt-created volume mapping Given I map volume test_long.tc as tcplay_test using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 1536 | aes-xts-plain64 | 256 | 256 | Scenario: Long passphrase TrueCrypt-created volume info Given I request information about volume test_long.tc using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest | Then I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | PBKDF2 iterations | 2000 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 1536 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Long passphrase TrueCrypt-created volume mapping with trimming Given I map volume test_long.tc as tcplay_test using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttestexceedslimitation | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 1536 | aes-xts-plain64 | 256 | 256 | Scenario: Long passphrase TrueCrypt-created volume info with trimming Given I request information about volume test_long.tc using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttestexceedslimitation | Then I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | PBKDF2 iterations | 2000 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 1536 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Long passphrase volume creation Given I create a volume tmpvol1 of size 10M with the following parameters: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttestexceeds64chars | | pbkdf_prf | SHA512 | | cipher | AES-256-XTS | And I map volume tmpvol1 as tcplay_test using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest | And I request information about volume tmpvol1 using the following settings: | passphrase | testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest | Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | | tcplay_test | 0 | 19968 | aes-xts-plain64 | 256 | 256 | And I expect tcplay to report the following: | PBKDF2 PRF | SHA512 | | PBKDF2 iterations | 1000 | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Sector size | 512 | | Volume size | 19968 sectors | | IV offset | 256 | | Block offset | 256 | tc-play-1.1/test/features/step_definitions/000077500000000000000000000000001217612412400210675ustar00rootroot00000000000000tc-play-1.1/test/features/step_definitions/libtcplay_steps.rb000066400000000000000000000170671217612412400246300ustar00rootroot00000000000000require 'rspec/expectations' require 'expect' Given /^I map volume ([^\s]+) as ([^\s]+) with the API using the following settings:$/ do |vol,map,settings| protect_hidden = false s = settings.rows_hash loop_dev = @losetup.get_device("volumes/#{vol}") opts = TCplayLib::TCApiOpts.new opts[:tc_map_name] = FFI::MemoryPointer.from_string(map) opts[:tc_device] = FFI::MemoryPointer.from_string(loop_dev) unless s['keyfiles'].nil? keyfiles = ParseHelper.csv_parse(s['keyfiles']) { |kf| "keyfiles/#{kf}" } opts[:tc_keyfiles] = FFIHelper.str_array_to_p(keyfiles) end unless s['keyfiles_hidden'].nil? keyfiles = ParseHelper.csv_parse(s['keyfiles_hidden']) { |kf| "keyfiles/#{kf}" } opts[:tc_keyfiles_hidden] = FFIHelper.str_array_to_p(keyfiles) end if ParseHelper.is_yes(s['protect_hidden']) opts[:tc_protect_hidden] = 1 protect_hidden = true end opts[:tc_passphrase] = FFI::MemoryPointer.from_string(s['passphrase'] || '') opts[:tc_passphrase_hidden] = FFI::MemoryPointer.from_string(s['passphrase_hidden'] || '') opts[:tc_interactive_prompt] = 0 opts[:tc_use_system_encryption] = 0 opts[:tc_use_backup] = (s['use_backup'].nil? ? 0 : ParseHelper.is_yes(s['use_backup'])) @error = (TCplayLib.tc_api_map_volume(opts) == TCplayLib::TC_ERR) if (@error) @err_str = TCplayLib.tc_api_get_error_msg() end @mappings << map unless @error @maps = DMSetupHelper.get_crypt_mappings("#{map}") end Given /^I create a volume ([^\s]+) of size (\d+)M using the API with the following parameters:$/ do |vol,size_mb,params| create_hidden = false s = params.rows_hash IO.popen("dd if=/dev/zero of=\"volumes/#{vol}\" bs=1M count=#{size_mb.to_i} status=none") { |io| Process.wait(io.pid) } loop_dev = @losetup.get_device("volumes/#{vol}") opts = TCplayLib::TCApiOpts.new opts[:tc_device] = FFI::MemoryPointer.from_string(loop_dev) unless s['keyfiles'].nil? keyfiles = ParseHelper.csv_parse(s['keyfiles']) { |kf| "keyfiles/#{kf}" } opts[:tc_keyfiles] = FFIHelper.str_array_to_p(keyfiles) end unless s['keyfiles_hidden'].nil? keyfiles = ParseHelper.csv_parse(s['keyfiles_hidden']) { |kf| "keyfiles/#{kf}" } opts[:tc_keyfiles_hidden] = FFIHelper.str_array_to_p(keyfiles) end if ParseHelper.is_yes(s['create_hidden']) opts[:tc_size_hidden_in_bytes] = 1024*1024*s['hidden_mb'].to_i create_hidden = true end opts[:tc_passphrase] = FFI::MemoryPointer.from_string(s['passphrase'] || '') opts[:tc_passphrase_hidden] = FFI::MemoryPointer.from_string(s['passphrase_hidden'] || '') opts[:tc_interactive_prompt] = 0 opts[:tc_use_system_encryption] = 0 opts[:tc_no_secure_erase] = 1 opts[:tc_use_weak_keys] = 1 opts[:tc_prf_hash] = FFI::MemoryPointer.from_string(s['pbkdf_prf'].strip) unless s['pbkdf_prf'].nil? opts[:tc_cipher] = FFI::MemoryPointer.from_string(s['cipher'].strip) unless s['cipher'].nil? opts[:tc_prf_hash_hidden] = FFI::MemoryPointer.from_string(s['pbkdf_prf_hidden'].strip) unless s['pbkdf_prf_hidden'].nil? opts[:tc_cipher_hidden] = FFI::MemoryPointer.from_string(s['cipher_hidden'].strip) unless s['cipher_hidden'].nil? s['passphrase'] ||= '' s['passphrase_hidden'] ||= '' @files_to_delete << "volumes/#{vol}" @error = (TCplayLib.tc_api_create_volume(opts) == TCplayLib::TC_ERR) if (@error) @err_str = TCplayLib.tc_api_get_error_msg() end end Given /^I request information about volume ([^\s]+) with the API using the following settings:$/ do |vol,settings| protect_hidden = false s = settings.rows_hash loop_dev = @losetup.get_device("volumes/#{vol}") opts = TCplayLib::TCApiOpts.new opts[:tc_device] = FFI::MemoryPointer.from_string(loop_dev) unless s['keyfiles'].nil? keyfiles = ParseHelper.csv_parse(s['keyfiles']) { |kf| "keyfiles/#{kf}" } opts[:tc_keyfiles] = FFIHelper.str_array_to_p(keyfiles) end unless s['keyfiles_hidden'].nil? keyfiles = ParseHelper.csv_parse(s['keyfiles_hidden']) { |kf| "keyfiles/#{kf}" } opts[:tc_keyfiles_hidden] = FFIHelper.str_array_to_p(keyfiles) end if ParseHelper.is_yes(s['protect_hidden']) opts[:tc_protect_hidden] = 1 protect_hidden = true end opts[:tc_passphrase] = FFI::MemoryPointer.from_string(s['passphrase'] || '') opts[:tc_passphrase_hidden] = FFI::MemoryPointer.from_string(s['passphrase_hidden'] || '') opts[:tc_interactive_prompt] = 0 opts[:tc_use_system_encryption] = 0 opts[:tc_use_backup] = (s['use_backup'].nil? ? 0 : ParseHelper.is_yes(s['use_backup'])) @info = {} api_info = TCplayLib::TCApiVolinfo.new @error = (TCplayLib.tc_api_info_volume(opts, api_info) == TCplayLib::TC_ERR) if (@error) @err_str = TCplayLib.tc_api_get_error_msg() end @info['device'] = api_info[:tc_device].to_ptr.get_string(0) @info['pbkdf2 prf'] = api_info[:tc_prf].to_ptr.get_string(0).downcase @info['cipher'] = api_info[:tc_cipher].to_ptr.get_string(0).downcase @info['key length'] = "#{api_info[:tc_key_bits].to_i} bits" @info['volume size'] = "#{api_info[:tc_size]} bytes" @info['iv offset'] = "#{api_info[:tc_iv_offset]} bytes" @info['block offset'] = "#{api_info[:tc_block_offset]} bytes" end Given /^I request information about mapped volume ([^\s]+) with the API$/ do |map| opts = TCplayLib::TCApiOpts.new opts[:tc_map_name] = FFI::MemoryPointer.from_string(map) @info = {} api_info = TCplayLib::TCApiVolinfo.new @error = (TCplayLib.tc_api_info_mapped_volume(opts, api_info) == TCplayLib::TC_ERR) if (@error) @err_str = TCplayLib.tc_api_get_error_msg() end @info['device'] = api_info[:tc_device].to_ptr.get_string(0) @info['pbkdf2 prf'] = api_info[:tc_prf].to_ptr.get_string(0).downcase @info['cipher'] = api_info[:tc_cipher].to_ptr.get_string(0).downcase @info['key length'] = "#{api_info[:tc_key_bits].to_i} bits" @info['volume size'] = "#{api_info[:tc_size]} bytes" @info['iv offset'] = "#{api_info[:tc_iv_offset]} bytes" @info['block offset'] = "#{api_info[:tc_block_offset]} bytes" end Given /^I modify volume ([^\s]+) with the API using the following settings:$/ do |vol,settings| s = settings.rows_hash loop_dev = @losetup.get_device("volumes/#{vol}") opts = TCplayLib::TCApiOpts.new opts[:tc_device] = FFI::MemoryPointer.from_string(loop_dev) unless s['keyfiles'].nil? keyfiles = ParseHelper.csv_parse(s['keyfiles']) { |kf| "keyfiles/#{kf}" } opts[:tc_keyfiles] = FFIHelper.str_array_to_p(keyfiles) end unless s['new_keyfiles'].nil? new_keyfiles = ParseHelper.csv_parse(s['new_keyfiles']) { |kf| "keyfiles/#{kf}" } opts[:tc_new_keyfiles] = FFIHelper.str_array_to_p(new_keyfiles) end opts[:tc_passphrase] = FFI::MemoryPointer.from_string(s['passphrase'] || '') opts[:tc_new_passphrase] = FFI::MemoryPointer.from_string(s['new_passphrase'] || '') opts[:tc_new_prf_hash] = FFI::MemoryPointer.from_string(s['new_pbkdf_prf'].strip) unless s['new_pbkdf_prf'].nil? opts[:tc_interactive_prompt] = 0 opts[:tc_use_system_encryption] = 0 opts[:tc_use_weak_salt] = 1 opts[:tc_use_backup] = (s['use_backup'].nil? ? 0 : ParseHelper.is_yes(s['use_backup'])) @error = (TCplayLib.tc_api_modify_volume(opts) == TCplayLib::TC_ERR) if (@error) @err_str = TCplayLib.tc_api_get_error_msg() end end Before('@api') do r = TCplayLib.tc_api_init(1) r.should == TCplayLib::TC_OK end After('@api') do opts = TCplayLib::TCApiOpts.new @mappings.each do |m| opts[:tc_map_name] = FFI::MemoryPointer.from_string("#{m}") r = TCplayLib.tc_api_unmap_volume(opts) r.should == TCplayLib::TC_OK end @losetup.detach_all r = TCplayLib.tc_api_uninit() r.should == TCplayLib::TC_OK @files_to_delete.each { |f| File.unlink(f) } end tc-play-1.1/test/features/step_definitions/tcplay_steps.rb000066400000000000000000000173551217612412400241410ustar00rootroot00000000000000require 'rspec/expectations' require 'expect' Given /^I corrupt sectors ([^\s]+) to ([^\s]+) of volume ([^\s]+)$/ do |first,last,vol| loop_dev = @losetup.get_device("volumes/#{vol}") first = first.to_i last = last.to_i count = last-first+1 IO.popen("dd if=/dev/urandom of=\"#{loop_dev}\" bs=512 count=#{count} seek=#{first} status=none") { |io| Process.wait(io.pid) } end Given /^I corrupt sector ([^\s]+) of volume ([^\s]+)$/ do |first,vol| loop_dev = @losetup.get_device("volumes/#{vol}") first = first.to_i IO.popen("dd if=/dev/urandom of=\"#{loop_dev}\" bs=512 count=1 seek=#{first} status=none") { |io| Process.wait(io.pid) } end Given /^I map volume ([^\s]+) as ([^\s]+) using the following settings:$/ do |vol,map,settings| protect_hidden = false s = settings.rows_hash loop_dev = @losetup.get_device("volumes/#{vol}") @args = [ "-m #{map}", "-d #{loop_dev}" ] ParseHelper.csv_parse(s['keyfiles']) { |kf| @args << "-k \"keyfiles/#{kf}\"" } unless s['keyfiles'].nil? ParseHelper.csv_parse(s['keyfiles_hidden']) { |kf| @args << "-f \"keyfiles/#{kf}\"" } unless s['keyfiles_hidden'].nil? if ParseHelper.is_yes(s['protect_hidden']) @args << "-e" protect_hidden = true end @args << "--use-backup" if ParseHelper.is_yes(s['use_backup']) s['passphrase'] ||= '' s['passphrase_hidden'] ||= '' IO.popen("#{@tcplay} #{@args.join(' ')}", mode='r+') do |tcplay_io| tcplay_io.expect /Passphrase/, 10 do tcplay_io.write("#{s['passphrase']}\n") end if protect_hidden == true tcplay_io.expect /Passphrase for hidden volume/, 10 do tcplay_io.write("#{s['passphrase_hidden']}\n") end end end @error = ($? != 0) @mappings << map @maps = DMSetupHelper.get_crypt_mappings("#{map}") end Given /^I create a volume ([^\s]+) of size (\d+)M with the following parameters:$/ do |vol,size_mb,params| create_hidden = false s = params.rows_hash IO.popen("dd if=/dev/zero of=\"volumes/#{vol}\" bs=1M count=#{size_mb.to_i} status=none") { |io| Process.wait(io.pid) } loop_dev = @losetup.get_device("volumes/#{vol}") @args = [ "-c", "-d #{loop_dev}", "-w", # We don't want to wait for /dev/random to have enough entropy ] ParseHelper.csv_parse(s['keyfiles']) { |kf| @args << "-k \"keyfiles/#{kf}\"" } unless s['keyfiles'].nil? ParseHelper.csv_parse(s['keyfiles_hidden']) { |kf| @args << "-f \"keyfiles/#{kf}\"" } unless s['keyfiles_hidden'].nil? if ParseHelper.is_yes(s['create_hidden']) @args << "-g" create_hidden = true end @args << "-a #{s['pbkdf_prf'].strip}" unless s['pbkdf_prf'].nil? @args << "-b #{s['cipher'].strip}" unless s['cipher'].nil? @args << "-x #{s['pbkdf_prf_hidden'].strip}" unless s['pbkdf_prf_hidden'].nil? @args << "-y #{s['cipher_hidden'].strip}" unless s['cipher_hidden'].nil? s['passphrase'] ||= '' s['passphrase_hidden'] ||= '' @files_to_delete << "volumes/#{vol}" IO.popen("#{@tcplay} #{@args.join(' ')}", mode='r+') do |tcplay_io| tcplay_io.expect /Passphrase/, 10 do tcplay_io.write("#{s['passphrase']}\n") end tcplay_io.expect /Repeat/, 10 do tcplay_io.write("#{s['passphrase']}\n") end if create_hidden == true tcplay_io.expect /Passphrase for hidden volume/, 10 do tcplay_io.write("#{s['passphrase_hidden']}\n") end tcplay_io.expect /Repeat/, 10 do tcplay_io.write("#{s['passphrase_hidden']}\n") end tcplay_io.expect /Size of hidden volume/, 10 do tcplay_io.write("#{s['hidden_mb']}M\n") end end tcplay_io.expect /Are you sure/, 10 do tcplay_io.write("y\n") end end @error = ($? != 0) end Given /^I request information about volume ([^\s]+) using the following settings:$/ do |vol,settings| protect_hidden = false s = settings.rows_hash loop_dev = @losetup.get_device("volumes/#{vol}") @args = [ "-i", "-d #{loop_dev}" ] ParseHelper.csv_parse(s['keyfiles']) { |kf| @args << "-k \"keyfiles/#{kf}\"" } unless s['keyfiles'].nil? ParseHelper.csv_parse(s['keyfiles_hidden']) { |kf| @args << "-f \"keyfiles/#{kf}\"" } unless s['keyfiles_hidden'].nil? if ParseHelper.is_yes(s['protect_hidden']) @args << "-e" protect_hidden = true end @args << "--use-backup" if ParseHelper.is_yes(s['use_backup']) s['passphrase'] ||= '' s['passphrase_hidden'] ||= '' @info = {} IO.popen("#{@tcplay} #{@args.join(' ')}", mode='r+') do |tcplay_io| tcplay_io.expect /Passphrase:/, 10 do tcplay_io.write("#{s['passphrase']}\n") end if protect_hidden == true tcplay_io.expect /Passphrase for hidden volume:/, 10 do tcplay_io.write("#{s['passphrase_hidden']}\n") end end tcplay_io.each do |line| line.match(/^(.*):\s+(.*)$/) do |m| c = m.captures @info[c[0].downcase.strip] = c[1].downcase end end end @error = ($? != 0) end Given /^I request information about mapped volume ([^\s]+)$/ do |map| @args = [ "-j #{map}" ] @info = {} IO.popen("#{@tcplay} #{@args.join(' ')}", mode='r+') do |tcplay_io| tcplay_io.each do |line| line.match(/^(.*):\s+(.*)$/) do |m| c = m.captures @info[c[0].downcase.strip] = c[1].downcase end end end @error = ($? != 0) end Given /^I modify volume ([^\s]+) using the following settings:$/ do |vol,settings| s = settings.rows_hash loop_dev = @losetup.get_device("volumes/#{vol}") @args = [ "--modify", "-d #{loop_dev}", "-w", # We don't want to wait for /dev/random to have enough entropy ] ParseHelper.csv_parse(s['keyfiles']) { |kf| @args << "-k \"keyfiles/#{kf}\"" } unless s['keyfiles'].nil? ParseHelper.csv_parse(s['new_keyfiles']) { |kf| @args << "--new-keyfile=\"keyfiles/#{kf}\"" } unless s['new_keyfiles'].nil? @args << "--new-pbkdf-prf=#{s['new_pbkdf_prf'].strip}" unless s['new_pbkdf_prf'].nil? @args << "--use-backup" if ParseHelper.is_yes(s['use_backup']) s['passphrase'] ||= '' s['new_passphrase'] ||= '' IO.popen("#{@tcplay} #{@args.join(' ')}", mode='r+') do |tcplay_io| tcplay_io.expect /Passphrase:/, 10 do tcplay_io.write("#{s['passphrase']}\n") end tcplay_io.expect /New passphrase/, 10 do tcplay_io.write("#{s['new_passphrase']}\n") end tcplay_io.expect /Repeat/, 10 do tcplay_io.write("#{s['new_passphrase']}\n") end end @error = ($? != 0) end Given /^I modify volume ([^\s]+) by restoring from the backup header using the following settings:$/ do |vol,settings| s = settings.rows_hash loop_dev = @losetup.get_device("volumes/#{vol}") @args = [ "--modify", "--restore-from-backup-hdr", "-d #{loop_dev}", "-w", # We don't want to wait for /dev/random to have enough entropy ] ParseHelper.csv_parse(s['keyfiles']) { |kf| @args << "-k \"keyfiles/#{kf}\"" } unless s['keyfiles'].nil? s['passphrase'] ||= '' IO.popen("#{@tcplay} #{@args.join(' ')}", mode='r+') do |tcplay_io| tcplay_io.expect /Passphrase:/, 10 do tcplay_io.write("#{s['passphrase']}\n") end end @error = ($? != 0) end Then /^I expect dmsetup to have the following tables:$/ do |tables| tables.map_headers! { |h| h.to_sym } tables.diff!(@maps) end Then /^I expect tcplay to report the following:$/ do |expected_info| expected_info.rows_hash.each_pair do |k,v| @info[k.downcase.strip].should == v.downcase end end Then /^I expect tcplay to succeed$/ do @error.should == false end Then /^I expect tcplay to fail$/ do @error.should == true end Before do @tcplay = "../tcplay" @maps = [] @mappings = [] @info = {} @files_to_delete = [] @losetup = LOSetupHelper.new end After('@cmdline') do @mappings.each { |m| IO.popen("#{@tcplay} -u #{m}") { |io| Process.wait(io.pid) } } @losetup.detach_all @files_to_delete.each { |f| File.unlink(f) } end tc-play-1.1/test/features/support/000077500000000000000000000000001217612412400172355ustar00rootroot00000000000000tc-play-1.1/test/features/support/env.rb000066400000000000000000000111621217612412400203530ustar00rootroot00000000000000require 'ffi' module TCplayLib extend FFI::Library ffi_lib [ "../libtcplay.so" ] TC_OK = 0 TC_ERR = -1 class TCApiOpts < FFI::Struct # Common fields layout :tc_device, :pointer, :tc_passphrase, :pointer, :tc_keyfiles, :pointer, :tc_passphrase_hidden, :pointer, :tc_keyfiles_hidden, :pointer, # Fields for mapping / info :tc_map_name, :pointer, :tc_protect_hidden, :int, # Fields for mapping / info / modify :tc_password_retries, :int, :tc_interactive_prompt, :int, :tc_prompt_timeout, :ulong, :tc_use_system_encryption, :int, :tc_system_device, :pointer, :tc_use_fde, :int, :tc_use_backup, :int, # Fields for modify :tc_new_passphrase, :pointer, :tc_new_keyfiles, :pointer, :tc_new_prf_hash, :pointer, :tc_use_weak_salt, :int, # Fields for creation :tc_cipher, :pointer, :tc_prf_hash, :pointer, :tc_cipher_hidden, :pointer, :tc_prf_hash_hidden, :pointer, :tc_size_hidden_in_bytes, :size_t, :tc_no_secure_erase, :int, :tc_use_weak_keys, :int end class TCApiVolinfo < FFI::Struct layout :tc_device, [:char, 1024], :tc_cipher, [:char, 256], :tc_prf, [:char, 64], :tc_key_bits, :int, :tc_size, :size_t, :tc_iv_offset, :off_t, :tc_block_offset, :off_t end attach_function :tc_api_init, [ :int ], :int attach_function :tc_api_uninit, [ ], :int attach_function :tc_api_info_volume, [ TCApiOpts.by_ref, TCApiVolinfo.by_ref ], :int attach_function :tc_api_info_mapped_volume, [ TCApiOpts.by_ref, TCApiVolinfo.by_ref ], :int attach_function :tc_api_create_volume, [ TCApiOpts.by_ref ], :int attach_function :tc_api_map_volume, [ TCApiOpts.by_ref ], :int attach_function :tc_api_unmap_volume, [ TCApiOpts.by_ref ], :int attach_function :tc_api_modify_volume, [ TCApiOpts.by_ref ], :int attach_function :tc_api_check_cipher, [ TCApiOpts.by_ref ], :int attach_function :tc_api_check_prf_hash, [ TCApiOpts.by_ref ], :int attach_function :tc_api_get_error_msg, [ ], :string end module FFIHelper # String Array to null-terminated pointer array def self.str_array_to_p(a) pointers = [] a.each { |v| pointers << FFI::MemoryPointer.from_string(v) } pointers << nil ptr = FFI::MemoryPointer.new(:pointer, pointers.length) pointers.each_with_index { |p,i| ptr[i].write_pointer(p) } return ptr end end module ParseHelper def self.csv_parse(field) a = [] field.split(%r{\s*,\s*}).each do |str| v = str.strip v = yield v if block_given? a << v end return a end def self.is_yes(field) return false if field.nil? return true if field.casecmp("yes") or field.casecmp("y") return false end end module DMSetupHelper def self.get_crypt_mappings(name) maps = [] IO.popen("dmsetup table --showkeys") do |dmsetup_io| dmsetup_io.each do |line| line.match(/^(#{name}.*):\s+(\d+)\s+(\d+)\s+crypt\s+([^\s]+)\s+([a-fA-F0-9]+)\s+(\d+)\s+[^\s]+\s+(\d+)/) do |m| c = m.captures mapping = { :name => c[0], :begin => c[1], :end => c[2], :algo => c[3], :key => c[4], :offset => c[5], :iv_offset => c[6] } maps << mapping end end maps.sort! { |x,y| y[:name] <=> x[:name] } end return maps end end class LOSetupHelper def initialize @loopdevs = {} end def find_free free_dev = nil IO.popen("losetup -f") { |losetup_io| free_dev = losetup_io.read.chomp } return free_dev end def get_device(path) if @loopdevs[path].nil? @loopdevs[path] = find_free() IO.popen("losetup #{@loopdevs[path]} \"#{path}\"") { |io| Process.wait(io.pid) } end return @loopdevs[path] end def detach(path) unless @loopdevs[path].nil? IO.popen("losetup -d #{@loopdevs[path]}") { |io| Process.wait(io.pid) } @loopdevs.delete(path) end end def detach_all @loopdevs.each do |p,d| IO.popen("losetup -d #{d}") { |io| Process.wait(io.pid) } @loopdevs.delete(p) end end end tc-play-1.1/test/features/vol_test1_api.feature000066400000000000000000000117621217612412400216560ustar00rootroot00000000000000@api Feature: API mapping/info tests using volume test1.tc Scenario: Map outer volume Given I map volume test1.tc as tcplay_test with the API using the following settings: | passphrase | test | And I request information about mapped volume tcplay_test with the API Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test | 0 | 40448 | aes-xts-plain64 | 256 | 256 | b736aebe0ba9e67f58f110769955a19d1351b07f99cbda9f59249e0aaed72f9e672365dee6d2bf00a34bdd1b8aafa31b274103b9e925ac568e60562ce0c6ff0e | And I expect tcplay to report the following: | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 20709376 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Map hidden volume Given I map volume test1.tc as tcplay_test with the API using the following settings: | passphrase | hidden | And I request information about mapped volume tcplay_test with the API Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test | 0 | 10240 | aes-xts-plain64 | 30464 | 30464 | 7dd9086e92b756f55465723a9b4594bf61040a6bea8b2291a45821ff80d0676dd86caf6595d57beb7902e952706fbb7bad8b69048c47cced1aba08ebac847dd9 | And I expect tcplay to report the following: | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 5242880 bytes | | IV offset | 15597568 bytes | | Block offset | 15597568 bytes | Scenario: Map outer volume protecting hidden volume Given I map volume test1.tc as tcplay_test with the API using the following settings: | passphrase | test | | passphrase_hidden | hidden | | protect_hidden | yes | And I request information about mapped volume tcplay_test with the API Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test | 0 | 30208 | aes-xts-plain64 | 256 | 256 | b736aebe0ba9e67f58f110769955a19d1351b07f99cbda9f59249e0aaed72f9e672365dee6d2bf00a34bdd1b8aafa31b274103b9e925ac568e60562ce0c6ff0e | And I expect tcplay to report the following: | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 15466496 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Info on outer volume Given I request information about volume test1.tc with the API using the following settings: | passphrase | test | Then I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 20709376 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Info on hidden volume Given I request information about volume test1.tc with the API using the following settings: | passphrase | hidden | Then I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 5242880 bytes | | IV offset | 15597568 bytes | | Block offset | 15597568 bytes | Scenario: Info on outer volume protecting hidden volume Given I request information about volume test1.tc with the API using the following settings: | passphrase | test | | passphrase_hidden | hidden | | protect_hidden | yes | Then I expect tcplay to report the following: | PBKDF2 PRF | whirlpool | | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 15466496 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | tc-play-1.1/test/features/vol_test1_cli.feature000066400000000000000000000063641217612412400216560ustar00rootroot00000000000000@cmdline Feature: Command line mapping/info tests using volume test1.tc Scenario: Map outer volume Given I map volume test1.tc as tcplay_test using the following settings: | passphrase | test | And I request information about mapped volume tcplay_test Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test | 0 | 40448 | aes-xts-plain64 | 256 | 256 | b736aebe0ba9e67f58f110769955a19d1351b07f99cbda9f59249e0aaed72f9e672365dee6d2bf00a34bdd1b8aafa31b274103b9e925ac568e60562ce0c6ff0e | And I expect tcplay to report the following: | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 40448 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Map hidden volume Given I map volume test1.tc as tcplay_test using the following settings: | passphrase | hidden | And I request information about mapped volume tcplay_test Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test | 0 | 10240 | aes-xts-plain64 | 30464 | 30464 | 7dd9086e92b756f55465723a9b4594bf61040a6bea8b2291a45821ff80d0676dd86caf6595d57beb7902e952706fbb7bad8b69048c47cced1aba08ebac847dd9 | And I expect tcplay to report the following: | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 10240 sectors | | IV offset | 30464 | | Block offset | 30464 | Scenario: Map outer volume protecting hidden volume Given I map volume test1.tc as tcplay_test using the following settings: | passphrase | test | | passphrase_hidden | hidden | | protect_hidden | yes | And I request information about mapped volume tcplay_test Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test | 0 | 30208 | aes-xts-plain64 | 256 | 256 | b736aebe0ba9e67f58f110769955a19d1351b07f99cbda9f59249e0aaed72f9e672365dee6d2bf00a34bdd1b8aafa31b274103b9e925ac568e60562ce0c6ff0e | And I expect tcplay to report the following: | Cipher | AES-256-XTS | | Key Length | 512 bits | | Volume size | 30208 sectors | | IV offset | 256 | | Block offset | 256 | tc-play-1.1/test/features/vol_test2_api.feature000066400000000000000000000162111217612412400216510ustar00rootroot00000000000000@api Feature: API mapping/info tests using volume test2.tc Scenario: Map outer volume Given I map volume test2.tc as tcplay_test with the API using the following settings: | passphrase | test | And I request information about mapped volume tcplay_test with the API Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test.1 | 0 | 9728 | aes-xts-plain64 | 256 | 256 | c0c4f02fd1f099388e80e387186b341ee90dec22083d1bcfdb402db247f5e829d84da25ed03101645953c4d265fc4e9be56a8c313527a6be49c82b67304e5844 | | tcplay_test.0 | 0 | 9728 | twofish-xts-plain64 | 256 | 0 | 48aefeb402d01bffcd1aaf6db82f92c5951faefd6abd4ed26f10b0c0cd5bfc189c3a34ece236a2c614d39b8e97d2bc42495382dca757b03c9bfb26bb9d4f3dbb | | tcplay_test | 0 | 9728 | serpent-xts-plain64 | 256 | 0 | ab2ac0b2007a0ead7a26d5560fb4d385ab4b59d38a7794f211ae7a6f91e97cd388d5bda1bf57be14be1b39bf514873470006c687c03041b5fbeb47aa5cc3a065 | And I expect tcplay to report the following: | Cipher | SERPENT-256-XTS,TWOFISH-256-XTS,AES-256-XTS | | Key Length | 1536 bits | | Volume size | 4980736 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Map hidden volume Given I map volume test2.tc as tcplay_test with the API using the following settings: | passphrase | hidden | | keyfiles | key.1, key.2 | And I request information about mapped volume tcplay_test with the API Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test.0 | 0 | 2040 | twofish-xts-plain64 | 7936 | 7936 | 9063947314787d4645bfb609461e8636f7f103dc82631897ed35e2aacf5d651266fb409395d337ecf91578d446cedb5f3979058c79399416850c512060bfaf20 | | tcplay_test | 0 | 2040 | serpent-xts-plain64 | 7936 | 0 | fbdfcc05d46e450accb6c7b17c58de8807a39ef1b61bb5347f21b9fc65e9bd3c13e7c0818df5d76a56055abafb8fa2dd3878140996172e418970857a20509509 | And I expect tcplay to report the following: | Cipher | SERPENT-256-XTS,TWOFISH-256-XTS | | Key Length | 1024 bits | | Volume size | 1044480 bytes | | IV offset | 4063232 bytes | | Block offset | 4063232 bytes | Scenario: Map outer volume protecting hidden volume Given I map volume test2.tc as tcplay_test with the API using the following settings: | passphrase | test | | passphrase_hidden | hidden | | keyfiles_hidden | key.1, key.2 | | protect_hidden | yes | And I request information about mapped volume tcplay_test with the API Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test.1 | 0 | 7688 | aes-xts-plain64 | 256 | 256 | c0c4f02fd1f099388e80e387186b341ee90dec22083d1bcfdb402db247f5e829d84da25ed03101645953c4d265fc4e9be56a8c313527a6be49c82b67304e5844 | | tcplay_test.0 | 0 | 7688 | twofish-xts-plain64 | 256 | 0 | 48aefeb402d01bffcd1aaf6db82f92c5951faefd6abd4ed26f10b0c0cd5bfc189c3a34ece236a2c614d39b8e97d2bc42495382dca757b03c9bfb26bb9d4f3dbb | | tcplay_test | 0 | 7688 | serpent-xts-plain64 | 256 | 0 | ab2ac0b2007a0ead7a26d5560fb4d385ab4b59d38a7794f211ae7a6f91e97cd388d5bda1bf57be14be1b39bf514873470006c687c03041b5fbeb47aa5cc3a065 | And I expect tcplay to report the following: | Cipher | SERPENT-256-XTS,TWOFISH-256-XTS,AES-256-XTS | | Key Length | 1536 bits | | Volume size | 3936256 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Info on outer volume Given I request information about volume test2.tc with the API using the following settings: | passphrase | test | Then I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | Cipher | SERPENT-256-XTS,TWOFISH-256-XTS,AES-256-XTS | | Key Length | 1536 bits | | Volume size | 4980736 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | Scenario: Map hidden volume Given I request information about volume test2.tc with the API using the following settings: | passphrase | hidden | | keyfiles | key.1, key.2 | Then I expect tcplay to report the following: | PBKDF2 PRF | SHA512 | | Cipher | SERPENT-256-XTS,TWOFISH-256-XTS | | Key Length | 1024 bits | | Volume size | 1044480 bytes | | IV offset | 4063232 bytes | | Block offset | 4063232 bytes | Scenario: Map outer volume protecting hidden volume Given I request information about volume test2.tc with the API using the following settings: | passphrase | test | | passphrase_hidden | hidden | | keyfiles_hidden | key.1, key.2 | | protect_hidden | yes | Then I expect tcplay to report the following: | PBKDF2 PRF | RIPEMD160 | | Cipher | SERPENT-256-XTS,TWOFISH-256-XTS,AES-256-XTS | | Key Length | 1536 bits | | Volume size | 3936256 bytes | | IV offset | 131072 bytes | | Block offset | 131072 bytes | tc-play-1.1/test/features/vol_test2_cli.feature000066400000000000000000000116211217612412400216470ustar00rootroot00000000000000@cmdline Feature: Command line mapping/info tests using volume test2.tc Scenario: Map outer volume in test2.tc Given I map volume test2.tc as tcplay_test using the following settings: | passphrase | test | And I request information about mapped volume tcplay_test Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test.1 | 0 | 9728 | aes-xts-plain64 | 256 | 256 | c0c4f02fd1f099388e80e387186b341ee90dec22083d1bcfdb402db247f5e829d84da25ed03101645953c4d265fc4e9be56a8c313527a6be49c82b67304e5844 | | tcplay_test.0 | 0 | 9728 | twofish-xts-plain64 | 256 | 0 | 48aefeb402d01bffcd1aaf6db82f92c5951faefd6abd4ed26f10b0c0cd5bfc189c3a34ece236a2c614d39b8e97d2bc42495382dca757b03c9bfb26bb9d4f3dbb | | tcplay_test | 0 | 9728 | serpent-xts-plain64 | 256 | 0 | ab2ac0b2007a0ead7a26d5560fb4d385ab4b59d38a7794f211ae7a6f91e97cd388d5bda1bf57be14be1b39bf514873470006c687c03041b5fbeb47aa5cc3a065 | And I expect tcplay to report the following: | Cipher | SERPENT-256-XTS,TWOFISH-256-XTS,AES-256-XTS | | Key Length | 1536 bits | | Volume size | 9728 sectors | | IV offset | 256 | | Block offset | 256 | Scenario: Map hidden volume in test2.tc Given I map volume test2.tc as tcplay_test using the following settings: | passphrase | hidden | | keyfiles | key.1, key.2 | And I request information about mapped volume tcplay_test Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test.0 | 0 | 2040 | twofish-xts-plain64 | 7936 | 7936 | 9063947314787d4645bfb609461e8636f7f103dc82631897ed35e2aacf5d651266fb409395d337ecf91578d446cedb5f3979058c79399416850c512060bfaf20 | | tcplay_test | 0 | 2040 | serpent-xts-plain64 | 7936 | 0 | fbdfcc05d46e450accb6c7b17c58de8807a39ef1b61bb5347f21b9fc65e9bd3c13e7c0818df5d76a56055abafb8fa2dd3878140996172e418970857a20509509 | And I expect tcplay to report the following: | Cipher | SERPENT-256-XTS,TWOFISH-256-XTS | | Key Length | 1024 bits | | Volume size | 2040 sectors | | IV offset | 7936 | | Block offset | 7936 | Scenario: Map outer volume protecting hidden volume in test2.tc Given I map volume test2.tc as tcplay_test using the following settings: | passphrase | test | | passphrase_hidden | hidden | | keyfiles_hidden | key.1, key.2 | | protect_hidden | yes | And I request information about mapped volume tcplay_test Then I expect dmsetup to have the following tables: | name | begin | end | algo | offset | iv_offset | key | | tcplay_test.1 | 0 | 7688 | aes-xts-plain64 | 256 | 256 | c0c4f02fd1f099388e80e387186b341ee90dec22083d1bcfdb402db247f5e829d84da25ed03101645953c4d265fc4e9be56a8c313527a6be49c82b67304e5844 | | tcplay_test.0 | 0 | 7688 | twofish-xts-plain64 | 256 | 0 | 48aefeb402d01bffcd1aaf6db82f92c5951faefd6abd4ed26f10b0c0cd5bfc189c3a34ece236a2c614d39b8e97d2bc42495382dca757b03c9bfb26bb9d4f3dbb | | tcplay_test | 0 | 7688 | serpent-xts-plain64 | 256 | 0 | ab2ac0b2007a0ead7a26d5560fb4d385ab4b59d38a7794f211ae7a6f91e97cd388d5bda1bf57be14be1b39bf514873470006c687c03041b5fbeb47aa5cc3a065 | And I expect tcplay to report the following: | Cipher | SERPENT-256-XTS,TWOFISH-256-XTS,AES-256-XTS | | Key Length | 1536 bits | | Volume size | 7688 sectors | | IV offset | 256 | | Block offset | 256 | tc-play-1.1/test/keyfiles/000077500000000000000000000000001217612412400155165ustar00rootroot00000000000000tc-play-1.1/test/keyfiles/key.1000066400000000000000000002000001217612412400163600ustar00rootroot00000000000000idK\Oǯa[nZ+{yUg~η3tp&)߈7VRn hJx (ec9c:KX$@ۦyߜ8Vʝ调٣Y]4=yf3K)ȣkG24x#!+إ4Pt-}͆4,\J@?@ALbAJ[ZTl˙\0boŵr)1IZ>-ƙefmPP4A& R~qWȸPe|6".M H=j4VCDn^Mo"ܓSy_:^{۫awq}: 0{]-Y?uݗf]>H$WqjD1yNjvv\"[3q4s 84\q(|oK H6V-ٵ'X_=,i+4zdڤ uP >C W6uttrlM4DoS 3Ao9CN3ZTďXܙ@MG1$lȐ;ܩy(fC l ?xN]_'uw C 9ԛ?Z+^JLzH08t/^Ҋa8?\YZNIqѩ4B;Zc@DX{GNVّ>L!8Z2Sb{ưꔥ`Ca\f>%G<], (p ^}bW.?{=byݹݚ]Pؚ - Rjz=iƢ6HL9?Lnz훝I~nrDLj> @Gl'0v^]tPL{e h0ד䕱0+DO2_YāMbJ[PlrHm^đ ק6K9B.^%lA~(oŨO^ߥ1Я^fJ> 2ki'dXDT Ȝ06fiݺ)4`TEG "Ȕc^O1g3"pm /P{yTNAtedGfg>C=>YLY g-WY%b {DdFo؁ C:}_2(,Ol Ш_|ܫ?T`zALӭKt}$OdTIܪX?Q&zn0e(H{% L+1uJ/X|;Isl]s?qo'Yi ݮz8rM_ÔQmoSNShS=~A1C81[āw-nՓ33t$8gv8%cPL{ fӃ3)mLRNWn$RPydf%,L2n&ZGdZ rj>DZE?YMUՀ DԤo)#z_Mf }.hL($֥V[|Fj8!\֫0hD "M}/N-#\̩rYwqfSL8\W)Z-Vg {{RnmYfi>/~O%}nV^ʃT2s}hn,/:⛵O@l.'\} cξ!+ObbghuGJR+Ȧ2(a d^aҁH T" %K 6\ݨr0"ϋ`g^4g)2OY O8W ,~bc0A˷l YtV7N%D ].8e܁!Iާ)Ի`-8h[sP~g,4ٛ/3r;ʊ4ljQjw;d1`Gc3 #G|ae;%ljlV~;?b;vֹw*2lfd RDԎlG2ȉ+<'jX&z|rC{+:{x9%Zٳe.C¯"t% Vi}>n4wQ);Z+3˿&1v\giO3{Hˮ^.ZGg${ nٹur$r"`(dM4I,9(ۻW+IcΈ3s/%ʡ~rkڄ3_DIk*ouŚwҝŮ0-T0c F䔒JbQ2qIǮ%5ϛnixd^.˲a,%}mLFX*ر_߶< >?C+e;D۴ISKX2Iij_hO7 ]T*_G^oKf-D-'Xq s"Ai`aXt[dx>L,p#eT\9uJ.čb^COJ.Co!f'~є'̕$ZRK}uj1m{>$1~3 g]xL`6(QtU>uX|ޕ$gY w̿LPAA_q͓s[/yIՆB#"ä_Njz1(YG+㚱%C)OF&(v2nmr0[2;= JhX5^9eL[#Ss[vU#^~3B2mQ1=t6*8iGޖyeo,- ՑApúUk|k`+TD xo+'ǿ@FM +W oTߛ>þkFO`3f_BCI/nVKƻp[T}&e33ÚS CM^\IIjx*jL(PlPŷ G̤]uoiK5tO fg6Q; WɆkx@EA#?ktw!tB -Q%۬haU Nk GSgeFv!%;E^ٰ i:<<&36oVf KWAnE`QͳG؆n\@YyR ׫ty;F?1FY gdM*B#p̎Egr%nVO\Rb!_Ef맓W-%8fU"jWY"ŸqI,$CJuYB=m|;MkG}8f[ kڣ|[6pD< rx/^\|Z+@N&RYR/0ɯ*%F$ k;.*N 3[aAP՟7~M XZ=J9 P WVq&N#Yz2/%Imk|7׺|DM7ռNEmwß $cFg@^/=&SQ[IS9Q KEi a: NQTL\aCq%31R+OrX׈6^8W_ hPfXj}J\NkZ~ƕxYk%SB:P&D ctVNlS$jN&U?dH_wIMҀ2{[,vϼ4L=7]W8]\yMC BkGb{O 9?7Ӂ.mUϵBDq>]:ϒZZ}W&Y\QFq9@^ć(>~; tۏ ) o6Q>u@ g`"&ՁV&MC(ry#Ľ I;^nP,'*6% Rct6Vݠ)'nJlkQU) U43$Dٓ\ߤdW*sY#u_N 9&U;i:t!C4 5Q̹=,P (H~ & o$(#}Dk '\PC&ȓm9 +#7}Sν(S^Lpi.'nma/2(C o"O"L/|E:!)V0mJ|m K]q~}3\}$zz*!鳁wBS6md26te7+R sL_4(Nn(yGnڃ^4B$?OQg)(]`֞sa/`2=q'VtTg*MTY:3+Dn=1! /&1T>J7h6T?Ȱ6ұ؟ ڊn'ΟE>[KRlMTn@Dw7ض l9h;$6f܌WP`K?UeYGl&To"MΪ%\}.a tC/3Rwl怀xhT@9营ƃ/-@!gz ҳG~yU3.<;ò7J[,I5%$Z_;HWFFF=ޯbPbgFF]f;'E}k|4MBV5K`%O˦X~!o.+&${ 1YNU/@5mUՀ;a܅MOR6'ĢE kbcj(/fx@<¼g/lKwDȥ߮uOÍ x}b'h+ȉ7-OH:+$jPo ׁ U#Kߕβ-SO߫UѢ1zIj\2hOgm\khwyZ436Y Сw.ŀi .])Nept8TX.)25H(Oh\ @ Еg9gބI.'\ݖrcj-yO]7d=v_ͻ^gk),ӈ23cUf.W#?ZCd1!Wغ4}@K 7ѧq>?pC `G, dx#r -d גE8%Rc5bg_B Ɔd [;=33t U t3֜ƽ2dHd窍jfDg(+epPu}c~&ceW 8y.yfMetV;fSfg]AOI V4etv8KuMp 0z W!A_k\P% g `.BK=ljHNjb"7c$Ch!$GĠrx謱A1L1Su 5þ]~jfV\'K MkbKW)/ HbDB̆2-Vz9~7n2y+Fn&k];| ]PVa׈j⌠˴Ӟ4 {,ߛ?ѹ u#} 18c+1(AӨP|a0U m l{\X82ذ7T37uH`o>!kUhO"AQ}nv*MK?ӗ͘m]NN321*edJ sVdXX/kM}Eje Qo]E+ŧ-tL+ <]6J;r.mpZFrM방qKrvKlqSh().ڿ4Xd*  .w f}|v0&#kVmTd?z(u.@QX؁ۃ1SZ2R?菶~_=̭g PNBVM 5@P(h+xNв:NqDYp gKv֏d7zٜWl!{$tEҁ^of2\#ZX@ _ǭt̢cz-4E2̝g-68{Pɘ"F=*1,EkMkLԧ)uvhƬ=~{rCөNpϙ>]yj>%qh2b_3s@3;?`H].eKǙ}Hdvv5oCoZL +@`5_(CS&pa[yŒP:Gk5'1GOi֛Ȁr"48 مY]#|<)d\\#ǤC.{hzQ׆ o%e!ȣdݵmj$:E0]%oWS##FPwd+o.D&PyM /DyS.Pp"z}P&(ZFNau#^a·LesMlw1#b;S6ɯc+yJZfSӆWthaC\'bqhjkbP+˂-}>VDFN# :zy4zSt*\AۯԥJ?o}=Sжr9pSV$:]˧ j8SOO*#|!Q,%هS69 rVT3*0tgUHn~w2/Z7lYO& _0tjST(t 'G͐*yN ඵK v[pqƎhˡ4-g!S+U39 O E#s&6꩜PƫJ!39*&EZ'(:enIbfcB{3ы/(l|n W֗m5ɑE[%OtUjn?P*1@Ŝ hzkǏU9h\;?d}LRjC4+=6 8=IY)17NU% \0E]++>s:4x 60z2VkAlB1켿+|Z`LaZ_Ci8V{u֤t J6fcOOF9%T'|su-³6j_'L "4b[sRPԞ9%as[I7PrG#TU傗b "՘ !z _V5"^㬰ſvUA6:g@as޻n2U: C\0,Px 'q-su:p3BEn;&=ļ$,=C "fUڸ^6Ki-TMø" !q: X7C)^mOG\e +|LL"e[hVtd]{Uɾ {~&%sZroB"wOz̼.÷iHxRԓ5*ۺSWa1o}DCy)v8/>y z}$*xiǙ(hXC,϶=l$`M ͏5Z[ (@9<ÿ=XI Ş-_>@sXl8`M1~hA+y7^uJج[M_a0i`CUkGǖgkh'ffm.&"`*lb U\Pcp0<"-R{*qKmh-o ̳xshI* ftm5k( a[[BEG;VT@be;҅ķ媱mGXٜ.+g EM&GFuhRJӇZ^D9֚.j1: v钕3 :4wS~{(%Oͬ <$E1eT07 {MӮrGEZ ϭl÷ rj`ͣ>},̓ RrI}k@FXmٕ'Z4Ճ۾&HqTlc3([7Iz muhDlCgaEպyMq,s@)n4V-}2*9YT ϴm^-F:tX>"4'|9NĊ߸Im 4[!`OgTaǽ樍{Ab37Y2t):6'VRe|^!![645+æM\˩iYz?~.cm <^6֫'JR] '$cb~#yy_ UM:, d^-Y-o՚ l> Zl58%)kBQ {5]Ԋg}P}Jלs4 r?;iV`U\&RSNYN=J 5YQ*[@NKYy%6<\9a h<3IK0";dJ[A&mExwht$ie ز\Mhtp V.zW jwW\*EpF?-It: w7vm}j AG[x//fZr` ȚBI6?= CKEЉXx N'd'|e{ _Cy46-oP! xgc?-Z %:q=9Yi ()iT}j4l,:Ҋ=6*O˚z5ݺX' J`tzlMCxL< [ rǮb2|?n\-(˜qGM)3j9L'\ T3+AwgA c+C1D/7 > r-2ڎ(8^M ˁ ~kh6x[-"ؖye{",9hh᳼Ȋ̷K^4/?_Il4;W1`YV81hJh#A:yO]ZS*6bAG/`Pzws^Eha~+0\]E eYȦOd@@+-?K!)#$CmǙ2vÀ*>z2 yؙ\|[jl~ZG9HcZ(vI w`p/龜#NmܶyaNQ;hץtB3TCUD@AclDz K#G述;P\yG asI;V#\l_TYSD8_K#u%;Pj1xȘPY0`ϟ~}IcIt$sM@Œۆ i$Bn9W藬t?(gV&Lp]3o;AI!bg_y5xvF;EB2ZeOV#~o̻Y*-˻Ԥ']4ږck8J#lS&"M ).x{U_Ey62H- ,hXfBجFC OErn1ôѡr|OcxmFyho^Ʉb@e6V4<=Q@jpN5-SA) y{W:XmU x q z9%IJ"h[f FOd~6u6QO)Rt\{%'؝rX&KaWek|ZO$ArLzQt(&D/,q/RH{fGw, 4>A3yݞE f#OD_b? %.O5}Č7;S\8iMX0*)_/$|/S[k'awKm?:;WeAk-U 3߸IX^u<WrRP! 6BDnaC+PHn`dϜr::8{`@&CUNuCm/"S^57lSP?pqܵNrYqseYOMP)ȩRs.{q; Q^72&W#ʾؚzdU p4XE+psnBkW@{ V|އ4)iC%i Q[S붵(7@;(/xhu- MWXݳĜN8mBmk>bKA{o{,IJ}; 政;=mjf3xLP*~sن:Lg $:U_וK`5 5K 5,1&Yhﳩģaޘ9َIW.d;3 OILbZ rQ~ qI; D,[ AuAM |xP7ižO c2<gi|*Y]6#v7\=g}ȵ8qTѳ ?uL@r9TVvن܄`?NQQ{%.Te=1.yl򺕴٬7`m>by'U̮C@wXS}]!zi;/uR_\=u P[8c-4)U/C9e^ۙ4cRpP|6گT}𳒧r<ęz3Mw/QgOcep}tNhW㪣MbE˱ݟ}Jw-c!]!̒O9BT,e5Z! A23 %ӟx;y Ct{@ZߥJŠ!قoފe9`QFRک $d1aI[~wi'h] 3J;&\SgZ貘uBr[)Qݳ)~%*5J6 B+ĥlp%!1AQ}K ~°ZJRr!ϑ!K(mX &(!#_ 6ɐ_p/W[Awڃl -8W1411"^^mŲBX 1b;yh7y:>}lx5F4!LjnWe&XX\X}6b^vg> ʎHF̵XGD ¡/ M ԩV"lC"ִrhz9G)_DĊD ܵ-(JSI~yLհ*D*%JꨒCi;pćH;mPO@.|^L Z H-T w7&Y AXQT2=gB-,&Zqb?#XȤ6T_0C#JDf\ SӪ iDvn5IyX֟0 g^Mi!p]2 BͿ5su[YƖ<}g$W2OX2װ9١0/_RBY |\2٧znUA( f;39̯ / VvԈRo.ʛ F7Bу2M&Rt'殮 TFS9NQh~)ٙ9CҮ}@ *5Вdr. Fѳnm8O"*]Js~BU~dy-k> W'w^Ne4?a${ٱ2oA)i@0+W5]8a=d \2rRIyOB30 l{%`FamCtYhZ4_>Ի>3Rc}/%6tCJ߰j̺%̓Mir盻l>G@쇔Z I/.W{gw,FE:Zj{-NP&>]t}] qfMCT&n[ GBwJO# }> v$A]MJyKdx;wXB1)] e!}M?Gk'சW 1XKQ$an9X=\J8BB/:Ad N),6>Zr3O9KMky 6^%$_y(wF_YYI%rgHѼcTR!gC_6yZ9V(Pd,N]`׻i*Kr}&=0䆝~f4 ~?\NW>E[љ^E% 13mӞ4L)EeY6Ի30Yt81үQtf-H-I2tvl$eM9ڤieH‹{}yv9g_g܉PhQ9a$T+!:JƠe?جmdqzdrUF3bH5f} MZ=ByXfi@+dn ?{;{pgOC[@ȩ=@NkUIl穇LF_M?iUbr#@`E#SSUxMt̓|ò3<x@$6L9h+f9-X;BrᶢCnPL9xhPuXJbg$H%&eY텰}H)LWA}J҆y)xoVQ)s l۸t"yڴOs 1 4+3;D$r:eܚB\]Da:ztںKYZ|7|V:6qZJpV#'M {S|҈}VZ.zy1R9{P$l4Ekv^m,rsmӗ)5/6\rm@IrRa6 %+MgG|C1HEA@9:mN zWv>Qjs(;`86 Aė!f4B8`J3-T'6}c&"8xV1rÚ2$6^2nzg~4f y'U"L͸= ;-S8P ;(SdJgA(@fuk0i>Bi)gT@n2(cdxMqEs>ܺ' jjnjH4 UsNme\!>X@Qv9D7ӂ|!XU|äQ2km9veE&`"m 7]#fS^j8)S=%G\y,z ԫ8#"A'D׺HbWsz1{6bsf^18|w>0uh8sL, / `58MIځ~%GɌPؑdG=J/=Ak%-A]yWr6b`a^_`v Cl>0rST )]8 6r*dRX IWZ4$+`s6NJ0'g? =0J=As K,#u!*;zkRs1_Pdo}in5dV&k9P,qg{,9/a%:b9?Jh=+Rی8nr;h7Mh^얇LjYcaSR²ԌS)nhQU*t:Pe%{]Eε`VqWF19t)Af>?`s#3g kWN~ ؞lϞ{=q2 ɐUK%9X!o}^!e純ENneũb:̻{:)' ً E_KGjnyd2 FqɃq4鯣轈NuV:$ҙ7w1ⶅ,u_-eʛRW?7u ' zy߷)5*r$'iш]}EI(c|O8Y݆ͫ((*wձեkjxAm@mw:ڈ>MS2KcJ6JZ|ʭ]<>ϐ…Iiiɛ `chejIZUc%0&c+s<#~M)ӰkZC Q>P0dY5~^&m%WBɴ*H$ɷth#pٯ8<&u)ͥUWee|M&QxyƼKL9fs$wwl@m[6mv]:q_܆qb!}0qx{Q(ꈮ5,q%ч .gcؚx"^{L,>] .9DeԹ`2.h{e-/TNU vr/TQ1 ħ!EJc I8׷MQe>(/9=f|n3B+oL5bsϷH[a&xb~LYLDE6fVVDivԕB_ #<*ˇx%)aDw2?qIvv0m`@HӾs\lQ&J&k_+v}On>2+UU. _K`%Y Hw{?;u"}ҡC`T'a"P4#8O]5/0if`֧lM1+U.ANIB)kU(Xv`4(Cu//8zځwJFvL1&ͨuO[]JәXّ6ZӅ4EQ%~ߤHD~Ld0K2, !x[E5* |qy&o!K{wM:%!N\`щ ]@jGzHI $AW,gج:7ͳtt;9ۃS?ص4@ ў)x#ь1lfLON3b@ȞJyOMxNZdqhмW:pB5I0)|dQL~ݜ}SW}ZE?UTU9ʔ82R PyF6Y:f%S&~͔\OsVs 1d+߰[+y͠Jk$6" ՄG7g律Zq{qeo ່0r-YH .׼w4n12D>gB"l==u**XߨE,PwM [Ƥu+%U"%f_-RN-Ӝ1,%\9E~ 6_[g0D_R/R# *3 .:7ꆙ~RL)Ji30%샂?-EOHɁ0&עE(b6\SZ-Q)T}y$_EBrzR&[iЯ[RJA3FD"jKf~0$g!FMY)9cLKDIRlLAje^lWKt͊LL\By#1*` 2EQߩY?B85V0J Lxs?Jxla=W$_<`$ ߋ)wZ9̼ͦnnTTo*M+CvO%SKۃENVfjL]Aƙz `'C<l'z"` V{\HAj#j$PCVi kן2$ o?3cjiLPZR=MҙfS[Dh\T8}/)H:]0x mzPъ2_8cI(ػOY\BV=AGw&hp<2j%Ns[s3xzۆ=v!;)7qa^K?BZB\{(JdnS[0nY dt.Rn݄WJN Wj6gαE7< GvvKD35̫*<\Q+%73 y}߷j6=VbI/&}v9e YBv[>=+Y4p3>;Jxט ]XYns!o_)%S1#ÛHm%Z ),l^LܻMA)5iRQ+9'] nA22n*^}AiR`pF-[{LjA>xJT?|oZ pxc'`j_Zф(#, 8!>S(hz2^ xEW8Dem$V*io :.w)-@2ﶬˋ![L3UnYae2 {~;6&2Q܈_shХ<&i"=Y^G'}}gO?O_m5(!6U3 +0|kkXmcܳs{Mu0jݍ$l)JZ#rmo s21s.u{mhGQbF&%"s7Ff- mCP_g[ a߁쫩9$yc74o4XB sh%5e0ۏqGz| h~ՠ\œ`<-#K-@.em?T%Vr t1hӡ_{4{AmܭZ !>XR~΅SxqYhV[bU/w/ ~6D/n4n0r^Be=2\zW 9̒Ɓ\p$\4QuOA PRF MU2yw K .j y`5:@!xW^}E–%C=:*z.c~u뜁u9?k4%[]ǫBdmkt*<ԖnwWwUK/L>l4Eˇdԭ(ăKev=w40+y]R# M JUGvNi*{^;b|F_g-_ǸP=fx|c>KS\9Fl~" WP$&)cH\C(jc48UXqo=BY>Xa}Մ`DBtF!\$!z }Ӳ#e )AYO78>[:a "X!wJ"MLkSv!}V&As>,)^8,wmi6}Xe@zGxC(j41sS.Zw,W'ef %(1$_<1jcf&DDrV,nMzW ajpxN"+aI0c 1AR"QMwRʷ[Mŗ Z qe߁I0;q/1J 0ۡCD9u! HALI*f+HFmCsP"?Q۱KK0хڪ- KRC zv(_eS%:v*Epæ~UaRd :2C#}S0RI:DzF?_ՀMgʹ%9[D+*mlU%BJ}9 F*GRH(`X:Ks! GYL0F,n[LuKK/R${[)J.LUg,Ъe* /c13kf=+ gBVK?^ D12uT` b}f AOX UT- ޼ɚviwLiC Z1|VD1"5Dtq;t> qxKNRѱ u۰f1-@%>;.iɦ I5ү;̽ĪU=[k`XVLEj> eZ> "#LѾeR',w! C(O@F=/ͻBָ] qEe,*]}#@# =u{upTسbZN`__EJ Iٗ 1gqfqh` Vk qρT:9<# p3:'McrvV>'*_v y91&5><俀LoiZ#B`ijM 5 &v{F$@RmQl*űss0HfsAIt˜ *'iu6Xwc2xC c_n =[vRrug,`^z4ǛfJQ‚EArr,S^լQ].XO+`kAl"0 0npSIg||,% nkO:!m4gXf# 7jiD1|qP-rߘB|E-so'E5XR߅$#Oa,8ӴpCz9D(SG&`eӄS< E ϰҨĈˌR32ZPY?M;}~M.jTh%'_i\1x%Ȣ}[C~\|b^$IF,hU}Iq|0xBK-6#L5"@`=dŭ30$vl N'$g.&s];!gFf!nW;ݵc4fHbL75N_7;ʘ@u$'(]H%(^d=d&GOԡ-YϖGY`%hDMFENkPЫ׵w#Y98Ulҥ1nO,ba]C/@W5!ZGԵ}E埒2ilCd {wfD2*yS(ս@ܐR,ȦxU4f&j0;"cOԊ(b! ߰B0%40`z2bӎ$/r t ¿Jx0W9<-LЛhRZvJ#6 @3fxpXV[SyH,Rkgb ct`Dڦ 1] D㝪b%@]Ep'y9J'h.~ G޴dz}vn @K=溃6d!4. MG}p/&#SP$ M"lYПvFh&MQ4D9)TYirMu0RKTCԵI͆+S4Tr$(喼5`: u[*D 18d7Lެj߶!$e:݄ G#f˄Ŏ>sF0FVF՗޻ZhZ#!Zqpvc>| ZϰXR_@ SYNM۟tZ6yF#[8^F/9Ϩ9V0J@s=3wGLuOm&Gh~ˈ˳ Fyp ?jY$ꃂ}Oc0@YQA<1a;%+@G)pust7^KhHbh \P~ީ@yy]RF)%}%f2N-HhiY2Jb ,!l Rjnp\De~Pfy .mȫ2C+ϭf"4o=INKs힦Hb9aiRy$T8Lm\B@~FSEc3^V;U K+b}"ngОL) #ʉvե]M Atup9 gPQfߒoFnCKdW!( ֛ȂG-t ib 7tWqC; ̴z4_! ͟pCC`8MS<MI U|z&GEa_leP£R?HxiKvA5Bd'\Jq___po0@)Yͤ~a!cȻ]Pn j"*j fȨV .giјu$gcL=H,{+Y%%CLR?>ޔH2 ^43mswOfñM?ŠC Ͳ9pnG4Kt8xNZ<.?-SJ $5f 4,Ш҃=GHM$W?:sNR QFzXmrM&گH廌 18!9#}ϊ\H2|D̢ a;~w)?zlwluF@ku`'B'.wc/^QTIţ+b6xvFea_9WՋ@  .wxg$P [8}QOvySqV!جh?8MG~h2F7lT! 2v6j2Fap!16R$a6c6.q6(Z.#UI~Z}g#G8~RJRzY/VlTz`<щKY}%-ZaA/gq>I.<؛v9ڋ>(T,E îmYj{|K09SZeH2=j@%wi˚!O^e*4z%!7Ebk4awO; Y1uk6c@ \g;G-[b˫څ/So34/qec[I_-2ygu]utO2jpG\Zˈw);SIj[mM1Wq4v-vniu\(GYBSwdȊmoKZ៕^isX>sw;|! xB4 .@%7T`ҩ^2/-6.8k7 Q0ap)ͮ H ,$$8jв{d-b6bG[~aP@RiSlng{fwHW4<x .]xJ?g*XBRί )`̴[u"^4u5A+qiט"9#nRxmW++6r͘!vx ԷXLGX怉4cj39}IHSF->ڐE &J x[a1HE Nug;o72tGUg|D?Q7mϩ恝b\ _$2>q0WVo\ryѾ{4%-ۅ0L4C:(P#',!ո6YUzM*zz;-fǛeTۥHOj_{\p]Zg4b|̈˜(IBbyu&;v|8!S5Fl&Asΰ\F@}~}3< ,ʻ2e~ږyIkT7˙nN󭏨=C[WՄ *PqySAy%ج5Dfnb>WXAl׺ٯ҉3C&Ha˳qwtZޢi/Rf¦hk=oM88Bi hy`yr+("ORʜdEf+lNNv1IKzM@w\s9أ,vc:O3>-Z )mbٟ}PYE] ?oX0d J@T> ()%SP䈆LҦ6uhbu^=}4igBIo"¬ dYsڐ4Ҭ@PA?aZ o) (O> }#c @5l]VDDyU/r םQƲz"N{]٪SH/kM=e1iZ;uʓ*A~8j' ϭ4vu- (N=$@j̉{!5˕De| uPҚS#R7_w8Oo%ͬ|2=g@4㙀* /G_JޔY9LAX)w=ɪc,2*ŝ|(YI3U1C,o[r`xZuVYtIdnb$99jй>u d"IHW;B<` \(hpe:bǜX{A Li8(*1DBŸ}0^( Zڎo܄Vi4TPOEUəVa0!a\y_p?o5g?\ֿ6^n~=ElF勭D#P%hyàyTDJNQaT"s𜼾H7QAzfVĝ=)Q\Dgda 9G&0IT3U-QbʤV+!]VX5ˈ },}~^@Pqx2iծ@c/B  =έ?fS]ujG {;TebQs@SRDźĸFAJvxp֫ ?"%J rbBgIs8hp*]Bg"=s!Ƅjv~"X IS Bp,`-,Je59؊Eژ`oV^uKvm+=ג2C>х;$YJ:Dr;{ \~Nb ];0n #éZ7j\bnyJMڅ5H\)>Y'ƧQݿDD-UjIb:Dx8sWTʯ3>TFd"B>Qk2.W~FB5Ag} zU#7:Rdg3>~)HzP@9FC4܍a @.mOq|6.NH3N\@vL&QO|ۿ6K՝_y=&psђx> srgbZPhws&:D}]a_2OkRag]>j=Aw . t#S F庅#Db*6{wR5YS~ 3 x.߀vD>>p{]#F/?fNNvq;`"',wL۬P7*B_jì˚Vdh4hD0nŕvr̲`H9zp9P=3:|%#q4/T1|X=$ʋј3Y&xH4R|.-#> 0υLH&H פԢr($4~((/OCup^jy[+ukpzV((k'R~]`Ǔ|P{=G2]Mo: ѓvz蝦NxQ/(\%2ĝ cE ~YSJSH[S&8K0)ޞ%y5δ庉!GTfV۱m-s2+ЗUCN';4T$fOF0t5~a_V?dU^.d_(Crr*NnXm`2E6^Zd~H5a`}87dVh>v$3g'&ײ'?"n.+&oA(BtBݦ7,lLPuS-h3Vݢ>~C_8\LyRQ87,ֿ\G9rW}<(;Rr*c+T)b< LzX HNo&~-V`S<|}yYLtnkю{@1[&^?b].'X H 64cI,ﴀDoW?bg(,_`_ۻvu\8u|ңR@x64ʑr='owW8mWp$Lm m g\":3_VyLPZg ǁmqȸ4H2U!g)d#V~"t"Ec>خ6k f dҔC]Nz֍7/K4 ù9 70Bv5 Kq ,ݞXpɭy1eWCaX}}bto %eJM5ʡKP9Jh8+FW qS2b`E\H bq+J((4 J%Ngփ}+TMuSh$g3Nvba~Q=8R yĤ+:S൝v@d{- ?,ɫ_>~s]pCGb #볈uP B+kfۼ ORd5E,N~[%[bV'-9V Y~a V'x!P5)ikLGztLOwA{XQT&irP~Ύ(-$B,)\@/]ucHMFRb3v|ORX?zr4? 8#{FPeόtބL5:y`4wyJ,W!2X-kO6Wv ܰ>آkɅHXJVܯȭ!"R&/燏!fDޅ6רBJ1~8fz6;#RKч~z [#7wsJѡgVg*kfū, w7ҊP]!o/a!z,Zv3}c3B%xT T~r >ux[XNA ă)<oh,8|F8wG gܽa89 ]Cs2r ߁ dCXMIOz8ET PO.4B=僆vf^C<;k5#%SCUm2ϳ44|^A wX2->D}du{Piq宆}:KR]TWsA[tjiČB[wh|%]iW1!Ud #$:s1bJH " m8 cLNYHFSP2blx _ŽАJI,@]cV<޳h/#`,d.knJ8s8$۫V?V@nY3,_*'e#("Xp28J= {hcfM Ϙ}0WbC܁"cXGck<{ʁhXI2pXrx;ʦs/kTCf<jy- S.&.UP/Ks~^~ Y΢!$wؿCcd+P[@G)Svd;T[NI2m2EV2dѓkc6A7!Pn''Mpeӝd[*#"zy<9j{f2 DF{'C?j A3S( X;5>RsoFJtizY j@b:\뾻7 m}p"`&W9舉VcV}9_ŌK k4~ =i3-2'5gS?{)zvt W`P_Do,7N)})"8(b~3* >oʖ :V]2FdzJ> *.mO}aS\ (PeAҳ @fV-ifnu!MjoN,q2Un+ {OI"/ -x?MⰴsW-I(Gƍ:Vqϴ2ۜ0"5>Nĸ; ;Owk;+8+w}{y6M@zt@pqCA$B+TƆ$a $G[as޳@ۏjDþzw r-m&rWEd f9P F <EI'PܙYtz5d_`oAe ah̴$.`<ƞ"oݨ"9h=((٫BXf n( 2abmVpx'8]0ˋGÿ>ĸ{у~8q6s]YM`h*>sdjbȨŻ,?ZZWɮ4?;wy"q4(zJ h7+<^f_绍MS(P L㡭Xt 5mP]+1W:F!WFt;Bɡd#Y+,|Rn+ y ]>IW1ӭI ?c[ǿ_O/w:NnRdMOxKU jl09 e1O&FSR iƎ\o(zjb֫$7{zku?ähU}0EOwc%fu= SA`1KkD=Si`itۧkygSefvclڈc!Vq@N-\"HGd}AwPGeC W䴃aټd&xTH{Zļ\:R`leU ,Ui(O~~[y6_*>cMkb1{=Xd@1g!Jλr461\aۚ=;N ~VΐZ`^Ppe&cc_?/MQw*DepK3~d Rgmzzb/pifWɪ"zƒCnJjpW ǭXk%\:p?0^:ktiě%` VlVsg,:oLR4u^ZY^jYKg9rX u!rUGn -)-)^=S>@="TV`E~>7b$uFW,HP-c銺#UAdzHX]n lDQrREPְ·cZv!'3̪^d-@wvaO` MˇV_R\|_ڼ 0q8L鞗ta؁V{M-wkH8SNPSeWRƍ T+,-B~ۣL')եбZvzN$¸>Qxx0(ӑښu(d0lp[h 鬓 %Juù8hdۂ1X^? . ;a*(c49k; n <.)Sbɨ/wqᲪ|>4Vaoٶk*VEV$"n;b]@a'׮"1 'ky(PbG=7uX(e-~;pqι8e;^b#>%3p/GُsԲ>IE H-BoSx6 *"!=m$^'z (C |h8BNd[ Vs4SfX OI^Tʆ[W$zcdX7--R@]7)3z,F$0LahXʞ,JQ.$R cb*(km #Fl*rHW08dG}]2Jr9RE.B"8Bc4(=FD5_Ogx p%qH)3 pGsU0H1A}K+)m4TMCmI wO8T%ѻ?op.Ϊb!B'J`XL<1 ;aO ecʕqG|,U4X!_ܻ-j z4SBY--w )yٍ44.i"dAXm%J1JzgrHP\1dKI. _ܬݪR`@%Jl dL>I%Ra/["ӷ.d_/vtvFD{Ɉ OO@of\_`ssr*H3Yd7Za."l5/GE>RqQRvL,` d`qMn agCio+4~9xe<pƩ̟Ն=T QKͯ V{řkp_' :\?R+8 ۍ|;?arXURs&V߉;`d@zCfF-ʫghcoB' erm؏'Brn<_N iVn]H,^_s]JF ?\x!?PY&4T׸RmRƙb]`z\3K=l e0DTUN*2R>kI$TyZ5[Zld5U|B8Uz6yɨ,q# F2Ko'9 UtsMYcba RvYv)NhG6!6Ȳ4Pt_ޕo74Mw>3]pSՄdq,Af5֞{6*R1=2_|<~2*#+=. -Ow$5-vS9[l- PRdjDI`_,|%|'ƌF_2r$YҩNbegO2>ݲTaAT ∪QvX,'mf"0\dl) ZO0Rٺtsh5s ғB!U~m0D(ʕ?eV Jk0 'Y'_.d5V\p_W˧B3A/A dF6Q *F4d 4NJk,Az RnUt A| NSBӹi~V3c5}yoLĖ-+W*9|,Gvp,<#BrVj RfrƩ{қvF-%{ZjC[6g@BE)L(2i7 )r6N~1!;ڻ)aKƯqs'ڇ7/)!k4ٻ8Gj<{i}a}d]Ra]Ee 7"dⷼfOO=Ư#@M\2OPuX/s袞d/^ߖ.=Ȃۘ>DR~GE6J0ētn-D8-'$䇠FU):ջn=T%VgSV\=LY'9 _U6FbU8tg>o[axy[ H}nJ:$چ.e`iu`>,orODzՁ嫫^b"1{I\%錳d4t+%&fz`G ܞFoONv)C` 2%;O ?+sf ۑXPN&#,ebf۲@ARC<髴P=#d~;QK_5ԭUo#"?5m5֣tƏy&%π$2Tkwl'ʎ[`;=%l'(#"h#Td4."`rR60Y/*" RX@0ρe'Q7 |ܗKRA\̱X}_-,۰,S sbzwTMoo,xM`Wsv-fcyGhNmcqm7A9tEd~ g?3m7r)ՠ">?m9hFpʆzz=f5qMYwo[MNSޅ!w*/`F99Y!8h]vmd(c24 Wlf(P:xXgR& asمnr:Po`ib,T.1OPߊg(\Ωm!M4{CVvaR2!9[knǫSkDQ蘜v.K*`A93m aLk##ɡ.1^tt1 g']:'C(D-Y6yOM=Hm"XGaw46,Y/Ѱ֮]:eyBu%÷tQ))ye]8$xW%.zzZT>Gl[c\3z| qhMw}]$9/sz%K82Y;O%?zd~]s!rS$(lVyO^}yq_y¡PX֫Vk*@#A3dͳ3n}|ɽ:p7*e3X-u#36\mqSh8ZH?W FCJ,ݻrh5,Vgp C Mﷃ%DR :S`tcS|bB _+ CAl4CjPnw :/rHuauk8_eQZE(DN p .nOӸA.rGmrlS r}hpJf?savW2P$̢֟ݗi;(m-pu8Oԣ9 "01Rl.R\6AB+"@A2urt 1.UC$oqҽ5/%wjje:h:s<f¶fբsBh=KU|[ev`rPmHas($ӈDpKfhx{_P:āW6%XVP~,}5lVe)յ _t7K.r)MY!^6`= 3Q PbgٜA_+30dgx^?l7m4 rJPeR[ \WSMSBiib`"-*j+\Ԫ{*cUmlO\a* Varw}W iJf(`g]׳g.`WPH8@+;Yp_@>k ߒ'Bg uBPp#37<,p3JGg9`%{9*BsXP^ .$5S*mٵ%tB{ڳ+e/Jg.PX{C}ikce!xMIW 9p"5AmRBJX;>~9 a= Aog[^ 7 uB=o͚z(n4<ZyySؗq`Vš˰-&Վv7IWU`>>MlhiU;kVq}ҫӤ`XrX °XPek1d&}9  *e@M~r^U/!NiVҰ4!l53Ł ts 16~ZIYAG+[ |%9F ( ߄v1eS'!OLe B ,Seұg]mV3w Wx&qaۡR]l.Jl=8Pjs 1˙ofkVR, )/m(Wz}b+%\Bϭh<&[ u~j{d8 _g4s텪04?6v(ee\^l7yO|:jVӁZ͔ȡt𹩾:pkIEp)Tqh FK"0BڗHk?}p כ sl Ejz*>3pT7ƶ ["檈ň'EBK?4FQ뛨+ZS0RWR 2P}|Ch׼Ǻӯc3qBa=WFIe.@Qg_canY3)\ e(BMF9 4s[1Ն]q[t1TDI߿|?GLzPgHtݾf<5rhR_7SVwI%lPqCW6,ӘnxڨÈo2^StPۓ\\DE!Y13S ilBG d7F %*SouƐ- y'aHk-rDI y%F!u_ ؄}l=-"UyiOzgO6adaXlBG aPɝKDJ[ޞ:yVI؞%K("Ov&,&Kmze(4j*BCDYi)vr ITRħ~3B5(OЫj)q| 삋jzu(qpl/϶r#w5PYY^PxQbQ8BU{'+wqzl^KBsI0Lj h bގ^!pY6U6Yh-#B1@֖mV ]a,hP+z{:< Sy8KIE%&-5G}XW!P ShY%L 4e)_Wk1 rT#ҴOYR5"qvIJGw{pRuVdp݌8J֫hsNE+y"³b\yUn?wE l{7<ܪ`|IHPH& PVC>eT&>q9glcD' fyzE9T%uG}#NUC ?;} H*H0 L \QP,=$=^ezaS>~Ў?HLFlR"8g金/,B'{=[РYrX7+YlQ.:ZY)aņLO|44ppr 17dL6퀃i̽*گ|5,Ļ6.mxmy&B7&X{cN+On? F gORvfM/q]-R2Z "t5Ij̗Fr2:e[g?fK -Lr/|R(QAXL .˫*(̣{a\G{$$ר(9qHu IJO0DnM}Xb)J c\iT(Uv.[PK>=9>a9@VU7=C!wkQ)ͧ+{8uE'vi O6p茶Ew !_|߃5z-wU-PhJ:H%%f3[m&%_]{J|EApQ[Q]Zq%k%ɻ_t 3 >_ qs@,rOdޟ aiߔ5iJqRX#Wݴ0n(ԁ$os$ UES'<\Mx _Hȗ$J4D(6/ B/ޝO.mWݝ(@VJ@pzteM#o|W`'Q)]֝CAycEkkeu?`7C.dĪb X#yAM{r;7ngNiS_BZ*v { ᆱObwR䏴to,q@/ڃub-6aJRC eֺ! ׭䴮ת$o }ۗSin6&;.spG%e]%߉{5VqXt Kj%m"Mз `Ͷ踛lM')xKR8{ko8&g @J6U8]j :ӻq+|"a[AxJZ0$ 40ʨz}VeZ1SHqF-qU$ڱLf#rIESqrEvҰ&iHgțITDl.iG1GưH+V2k[~EZ%TzE 0Ygl7[Ä iø:`J;棊Egۻ]2-6g8¹9c{5~b.I4%M` xjcpMcF[b#{s h;f zx&8Kt,]O=Ԃa*f*'J9'^ٗs K_ v-҃o|_xWMW[TvCOstfj'x `cʴ"a@;֙NÖ[hq[J9U[SL_Ò' ET$WќD<4ثAtdn:~8 B({M6xV`c=IflEq-1 q^pNmk9eCfJ No Wzw6`3٠@ K3C AT@12 ʊӣGq]&@t?|.7I ὃ?q0(&]%n9؇e~ (ԏF 1k$ne0PY5\XÀ4w`:zjw*~!xy\ݒ &8jCΝ>l;$."HW 5;Le:K(4?BE,IowDw5Y),^č)+ wU;2)2tQ11|LPRS9z:3dAW"bt.M,1WC smj<K{fQ v^']Sdmu3Bmnhm2Un\>D)uj>0т K}X N@jvT\u E*Jϣ̌bc&(<Gۼ!9x&Ҹ-LR+^o'h4 ֡h,wBi])wyv|H:bb[%}X0H=%VX EޝKW Rp>][FL Ϗ~ü86[$tM$bS@HnK>j{!KKPU:8ʀalrz;YWЪxq`,CP\Z *q@G(`(D4ʊ~)7~AzN`aƾ/[.knYeM ~ M;zKwAb>es>qBY|irS5uI %g=>őTڼH-`]:p`4;T^ŧO.A/+ѵ77yzhi"Tyު)IKv|G_Z9>P TnFqx1RYHg~<>F}-Z.ur2 ǒ+< `N;հ *s}Vt0FjPcv ~.o }@? ^ǂ{f='*Y-ғlEi9]./Xt8&!eBȵ(R1 q}kHm Dqꯓ'L4o1ͺ17thh˹uGúwe !oGLZ,f b,Q:T,3_Yv(TiL5[뮜 唡dT~`3 &jam5,bulAC4O.3*3/_&uS}@Ker &) %j&{`,&z&QxzhS KwjT^pATK&D>t9B0mD,TkdlM"Şw0dn(0wT:PPm΂4ʹy8(`i\'_u\/eFYIf&FG`'A[0II.$C#xZ\Fgay=EX@ɓҺKz1] "~ы-ӳXHAnDCV.(ܤ 1B}uU>cz:{_@ק ?cc+"+8> *-U39O:|}%j@,`ā8`ENelHj'@W8+J\)[0"z7~pނ֐GzN=O Nm0h {&$31K(yX츳Y}J~K/Y b3f]~Q..TϞ辔ǠH )߂/BXrg(ԵNbsZnkD5WԹϓ,s/l{2P@[2,8,*w4~TqA@u`Vi@Rv4=r}=Խq hTOA<]2 ]o%qG람(lH&Bfuz͕spaڏ-ZFzyG掭 b>ٖDOSLժz0ܑkn'`etP.a,cHcX߅z/yI]O>U}z+LnAέr.u"";!9\^ {#SEc68`N :J 3Fvl FBŎn(h82IfC @xmPrv:la` S89>T?ڠYm2ej& u)l3u| [W9ڄmFߛh|xϾ/tZ)"-Ki3kMvaK|ruUPܲz"S ȋ3i"}tv<u^p :x)ڦ۸xIYIrL񟊫u[,Jp@T@a*@C~n)R Z:"jW7Z@V)9240S!uUy)!i#x[b (h~l?dP|"b {"bHYn>}Ğ)eIޜ`^sBnwm H! (0gfAn05<7}"7$3lz?l @LS;.VF"%^{f?'=Ik=迠xUX6候pp3o)[E] T~K|.@ċVy Z >tmeIpaLa e8!mKFl8uX./>GLC5.ξPi>bTCSY%%Y^ɒqg\B$6Cgc``3~A?kf27[LR [#y97 ;m37b>fhjLsB_Xwf]H˶6r. =b"!*ZNCDa43H.k78sCyhG}P[pguHW95% I)?F3.1,P$H S\`å`ǻJ|Z.E]`Vԃ"H>b " }=>.=ϴ;51H![c;TMz’_ā& 5[JLMـO}9e?a6-Ǵuϝ˱U_}!A<Ǔ1j~`0]o#~?߃]-ZĚ,O T6e!MwV-_ZC4V虸r)2b GDD-4.Oxq!l~-'"t?b& oGuOa pdjЖeyvjHMԊLA^$ AvtG#V2l FQX&R. _y|,. Mр.nNa[w0(>͙xf`U+}U <S/)=pA6e*;];enOq60R?Feno!Kh9[Ax2,9{!In0Ŧ*]a]1& kcJY*1ôP`ЉR]ұ>g$3|uTچ 5 `ХI^(zZ}m^cԃ+ι%نGrEpPlgpjT Hg4x㊲ATkStACFȅreL:쩲7}ONRWqjEm7,"<`YD/mu|dJ$=]5Pr{@d4e eqSżuo#ˬ\ݑpIODͶ]_0k+.$T j~/0?ss!Cd1n+R7-_-1b )g*Cu2`9j XpKݦ49T(?d[y} hٵ0,x/&뜽=X/ּ 7ĈmyQm;@R\p@=$Ⱦ y)Tv Օ7 >p?VݥXEsvQ.: hg?&V?i>(0v"q xR~j$LiD,VofWB4o 8,EM)ِ .c-BX-"/sxؠ^ߏ_gXR 3HΊ`(I$D-˧E>F_e8m2Vnޡ[amy)<3#VV٭TA )G#Q2QKof/IĹ8@Ș/Nu9¥vanE`D? T_ kY(7wq$cFȘBI{~ix2TT%>-/ #ĢC(6\u偒X{qlNQE^,ŝ &Lj;8Dc/8RrgK#gg՘59uHo(rh  [)q-I{؁dS댱g`lf`k[f^1P\$?yV# :aX@3)Fev;;AڍwXb>6=}DJt,;&5 ] އ})bl.2D7Yǭܳ\@pxh(a0x/{qX~M`mT)Dc3װP?ak*e+;"ݥ&{L?PqYH)ggsXTHyo U7ps6; vt{yƋQ O${E"Tt-B?anm+yR|ُڟ(;x\9*;Ym^L)2ީ.d\B8;mb\4LˍUHNw 07פчSk*u6_>I۔r'}QF9j}^_]bvﵭ,WR^ oq\7YjN\ߟQ[UYY7eySy} (xodܥ )Íysb 8tQ FHH6ksT|Q$#hz}[JK1בL VL5m{.Z9P$Oس`CR*`lF ᇹ%Bpֲ1?c" =+"ˢ;JS/g(.'ZHӯ|_]J/bThDb33a"qn#JbN )M|. ƭYCT[A. Ƽa5]#21=*R~T= T'ֲS@F' ˙5HF߷w:-n3#)4=E?U9p5:cg+#%!`)mw*P|}n0B \oI\xp/-E 5#w퐔`چеAP ɫz(Hu*mLߺ ?TUCO˲)VKxoyƆ\or V읹*"nF` e )$Հ69FP RbwE?\1r/5Mpʼnm0i]Ȧn^!KBnX|].ʡ. f/sd6? ek ׺/7C4@f'OURoڑP*{bC m $R nh<rG #"dÙFU~tK ;{iv8"Hu7@i?1Xa~^8>Ls)9%>,eXGS8&zfR&l+D.1 Mw/K;}#)^ٺf(N&"ـٖءAP`61oOr縈[LNRlઑÎZ?98IDJxVՁ#܃4+ŗ?Q]Qq8ڨ'ђND?9`3MtS~hCܐX&4֬wFM;t1: basT:4XFcE*Y01q0uJf@Hڅ0.&.cݳy8˫69>z {&7:!њʵN^yp0.Ы ʪ.ik=ti|2,@YI_0ІEu` ٬q>Ώ; .F_GQ캕07N;J@\i޿zpKO:X!8 B|$ąud{:+)A cg`sw "C;000o wXlAt˧pe}C_n#c#]%')kK_':%'cHwlccBdFڮSjS tg؍pmF$޽R[^@sVGe ]+ޙ_I5M$nxM0r]-NڠH(Om3? ћ<6$I): +\xsn 5}6!⠍>lBBo>S1jrD%yjM0 JA6ӯm-ыDcJVٺzS􈈙PB,h{Fmi fpϲw|'&!8E< ɽ (G9ߥ fޔad ׅ8+gH$_*v"}`<;u5i~* *o̓NzP]{Pٺ7`'4@ݳ)OwI 8,`+}|53JVWs%nİ 3ーxxvT ?G?@pAF%yFi3}V՘oW U;EJ+ɦq"`7}ܩln!0؞ȱpwl0jpӺ;qw2q< *'t[^a!o>{v>0gpUӞ:*#868Vfͼo>08[j'??wD%oYFnAT<2RF~ T^EYd4KvAh^ar UvgXEM*2kbl8xʅϮ#Q#sb?1ΠEs0z{ܐx˳ȃv艐rV2sJGHwUMTSwυy[5Bo* ]ƾώb4KT7.gޙpu5}Hyn5h8kݛJ Ӣj$Պ_o#gp(Vq6(+UiylZ<+g"ug7GcҜտ̴8x϶N$~˃LYY~~ 3d^VKmaIJ#i LAo L 7VGXfzGU ]kahʿG$O8cx[rCVЉ BAFR~J5Ju†V$KfݩH %w*Mk{]Wi %8VZG01dWpV#ݾws^tg[(W5H]xB!e~=ƓAf (#De%#Q( Q!)6-<:Oږ`.f?"6})sj%P0:?m}rurD@ZFF-wR*)zoa>(+/ftBDF߯5-\MuugFunw}x=.f`1H[R (bir[tyDXD11☖8&^g]/+Uk_ .;miMošKc9 F8+Kqcn&lrE/&?\TcR!;GC_!BWP_|Gk߲eN=zm葯wG_e,V8kŠX9R7&Hf&-jo MIyM$ 3!Cl1 H2Utc-play-1.1/test/keyfiles/key.2000066400000000000000000002000001217612412400163610ustar00rootroot00000000000000ĺ?~71XlWÛ>]\>8YqqUݮ7FY *d-Y׵G y!aF1Pl섙**t-:CrՖx_AΛ X41\ d ,g<^Bm,c`N|),.?HX1AoR*MHb@~Ĩׇ$k:p|$rwE4,.WU4LQJ(&_H sP;0G-?3޾G9eR4+$WgȲ"#M,}^U8"ٝu`K;jfns:9w9C3 ȼ$C!<0F?{U]E$J'97jO&H|Z:09J65CL`gÎGW&Z l"c8]NYN~ ^>bxQstQcH&u!+<.. d//A lq"fȆGXKa<S T/GXC[)~՜#<+ kdfɸp=C^SK?:O'RIWfH9d12Odɻ 4`!TǹbTa*jcP=|M/Tŧ%?t1 o&uQ\V ::)m(m`;}7џ 9RRnͫU.t-~z`a7:UW@ҫcO4Q@(f 7+WUs6h P̯/N[b J&!wq@+y{=.ځ5amyly<*kINk]k(j&&4c:"  Xyv G88Yš` =ĴQ4?Zd+L9SŸ # ^ST37=(y,AQg]{/5:*+El,t4CzS1q.a&ir㻱I){ cf Vs RԈK:|}xqJwWF6qoI1>ٽOH׽喺nZbzӼ e=̇cۯ\L2Òdn[ZyaI ܎q~tԗVv0L낐P38h PxvbguqYnYp^֥%NVSr݀ Pb\*ʄ!œNZ%C:u8~Xݯy|7w:4촟Cudʤ%s)sld{´ֵXb擺/8T4uUxݜ/ܛZpٯF +[ڮV>ָ_ѝćvS7"e3ߪ!g9\#$;R1 LX܂x! E)"w)^ JX~zniPZ9d^A񤘪uy嚴(!C:1n\mꌮ/h[C<Sf:[o\ NKx7y`IT%ƱCZs* b~C_Q'-hxi-wa 0YmʪmS@3w?ܙk{??-S^~+ 4BG*W@™?J炧mIhF$fTUOc@t#N#.I.(7'd+;6…~Ӯ7{ل8 KonGa0܂ŵےU>8zW {*h[!t?1(!izGĭj6LYeCrUH<[,kpm0a'٧s!y= hUۓ[J/n w.%%:ЉsP.Lh,*+%_Q6^݄iѩMB( y)ʣG!>r W^L{~Q3,j e\I ԐWx%/`eXfW'VVWKyEZSQo'aq/OLQv:%2^@z>?J̺.+EH4UDp+02$#17ϳSImrc5{ב`*ݺtN,~L<х8j:Rh*4Ax J3ߴx Sj7 jG55QQm֩s8,x\jS,.#gBOTWfQ !ʬޟbb^/)bPm쇶&Ɣ, d-uK>!g\ \Rh#jnA?Ibx#M(GDududNn19gN%Qai} 揙G?2i#.gl"XPh .&^9Q0z2F'fͺTtK:2,761t*dWq&!MȲ-tCaʉB<[u45 p|A#7زljU#o8KAp~X.Y+ x +h2Y,>]_8j2  t'Tq8O/޳eyLq Yje~ZczI; yaHG*(czwYn6J}LI,jKక6F%+a fb欑PQMN1”ij(vӚKMY<|Տ+QdRew2}-9G(+c-/|nh8y( 2,YjG@IEpMJl|IODz^5>0{mjpjetm0bߠ30olWYjC8 5a㢧B9 _zkOWnvlH8gq:?t28frbװ71)W+wjyȐ fB,p7G/W@扪O[܌`?RLy/R=s? 9.Dncu݆%`U F98h2>*\pVsP]P_! "rn$d*kƧ<@‡ ]NG#*;)ފ?l8;Al̛QƻFR3kJp(ܠ`+~/ 5D=JD=ᅆ:#ȵWUv{$F}eooef*OG'N[w%A^Q Tuzl\yG@wA&TBb/d;qLɠ5@ge A+y?5K*18hM3ngSWBi-M^JE[^QE`\OeK ?<@nAҹ;iX85d|%ƒ"XGs~k&X{2/86sk I~7l atVL%J4Ց(P-Xj!@- y@_g7d4nM8ĔCU` Gg[CMmjapʓ]R2/(`q~b6}z$)G2@#Bƽ;"nuˍ}Ҙ $%38Fy{][؀F9Pr[..mr5M JBA3KBhb_5-f'p`;b >*+("<NKGS',m8c?"3CtƯB0(0 Gd;LDa\s:fboa#*`)}e`vN@4XQ KkBT"t=ܶ6\((Cΐ+B5Q5ƭFNm3<-U;2Ȋ1"E8yˎ<_(啮gL"z8KЀїD0gi: M/*.mE0!yf/j' b=ݝ=*O# |ggl6 ޶MǿbfZX9մ^ ~CEPˊtJ"{hwRiOaV@G4Q.*:J4^aq`7G5'ѸMx|{m_-0SV@xVn2 gs,vaZnɢ]>rо H8(JQcb<~~uYC27ovߚ^Om]3- Ɓ Un=: ]EɥS,YǴ~Ҽڼ[FxU x[e喝4TOhR!NKky5k Q) ,SO)7fNo}~ &1he#HXmw7 B @|\]H5L?{i^t-o(oqpۆ3u9䡐j>^G˳*xh)gFۈAx [8)JTp%#>" b*p2)wg;݇cvy98<{6;0%A@AV:?YAd7uLA[ lp<QČ#RF#&xKBV;{j1;^ln#6SŮZn +9VA!w;d]&ӿ ]P H](GD;j$U,̠W_`9BB&@r "s;.g,f| 5*f\XoĕZ(^C!tI^1`6#q˿bjMܽDlP,H3n i۴f/͔Şsc{o (=U>(F%ɾJ6sP{^[No|铡\ iϔO@|Rw7ˬb釀@]((gj^g-z"C.i!#-SW`&?~kă210\7.Ju"9 zO4׶ xerd sꗩt'²: _=umqmiZR{p]>!+[; L[eIONP>lȩ|V[f14N&I kqj~eH>+Q\*\K2If3 }^f2QFէI)q0GpKTj~X:gTpvxBh.t֚#*<݇7.heh,F&%w]n? G%r\PhX[.gi= 6t./NU+= -HBJnYJ0@` ҍ]6Q\޸HӚy \#a 'hB-|6yŚ"ԚҪD # Lw"TWu 01[Β6'YDN\cE%2-eM#6eKRgo_ o\<[P1ӏyL0ˬۂRV5؝'VLzﺀVFb6C~5n'ن9^l8D!dp~l7(;Tw24v@-#PPj)֢5"#FQx=zcdQ%;_m!hx@0NY7,m)I`َfX;CeOB.:9Zxw oY{ q+cUO"DԀ]+jI:V) Wm- *H@=~#^/ :ҏ4+3_; T0YS %w%FU@[.>$ !CΤh4 Fe4XVaіǷxM= =pshr1*;*T@].,&[M[KBj%h4}חqf9R5v=MK?HbNYGߤS`r"hi 6DA_@&>gu]h&8>7|PM0t^8 aL_a$eP}RӲ|05D0OƳ"WxUi3{/VxC=n*E&IZ]#UqߢpSɳgX*h?##vʳ~/6[nA UvW51~}(" }xayrpW  tXzY9=] S]Ei֛6qTې̳g='r j&t::\.wpt#?]ѹ뗒3|@rkFĮ`L) |* Zq_}KyhS@9A!2N [fxsb:eX8= aCw8dp#y&^W/W,s9q.?I2,12@vʞCv"67:He'B-֥S _z[\M . n UY$Ym43RHѰ e6~IO?K {WIR3dY !0k@MB'=ŵMF #$FJ-͓FLko붗>Vx׬ACmsϗ)owB7lv9RDN&dIXOEɳy\u tX5;%T-֬$uhTܵLn&u(=v\`xto(n(IU006F|L>!=)o+twKp>,"a_ K*Tu—TB'P ή߯*.Vnϔ)UxOis6h/8ܰ29BVy+*1]GTh ٘U 2?q^T[Y&`>Gx[t19fWPyH#_%&Yʋc' 9_&~S- {i)2@=Ǵ(wq5QPgFH$`6 dF}|L"/+VC0k(7s Y6&?aG]d6 YlDQ!xs& K!?qo9T~QPJt jrH飪TyǸw */O&9wQ:#3/` )5OgA%+Xps9,+ް;|~ylM ZÐXv(mTOӲ6q=| YoJ7H֪$Dh"iЪDuF>J B]K,}Jpcӯ0f~o_X]4_͸x y}QxִQ2HX EHrwf_m( <򐍭Ͳ#`2\)?2#,Z}Waem7@R(puVg[}å6Sz_9 9-K6?rLf9sDzUԉ4fJ5>py(iA!6ynE/o@Õ}sL@GW ?6 5P[߳-SeʇuNOy 9gp(W= -`$Pl6e̪3ݩ؇eTffLuPP>"Կlu KjZIqAyAx s 4`' 8LUоE#.Wʆg'-L9x@/N+tL= h(G J8'̀9:(k LGfJ!@m\)~m[#noC\e珤degGn{ $#&ntf~ ]^tU{eSֱs_+"dSVA$| `Xs O0 Noʓ}7a/m_zeX Mvk/wAQą4eU*WqZ|[Ӵ9_#\$ ԋ-4j+³7ug:U = $n7ag.gYXp 轨$g ^Ka(*Bcj\&-ve~h^5מ" xrKnP428kH) g d; Zj]poŃyZr7Ms,X]Vp^B7A2v;􀼖Gڈ17:{K)JXKߚ/=T_I[~5sKm7|yh8nT`Zc'9.J` EBp^ _osui&r@1le\E;tD]F=1 yf/Ǜ5k]84p8t~;H ?fS#Qc;k@g"\h׉3hQ8ܴ͠E2 8|$$i#XNiU> P<s3 EDA}O?@Mc!ԅ|._ܓj7R LLo^跄?Gh]\!IŃ٘G\d^7BbE6%ECnPt_)m@3&I Is&+s0"LQo l٩@@e8h]6Mt?ry/r_I͏d)hY=q0:<•>)9H03z9r$o2:R>uٷҘՁȢ$:26hºpl@#P#O<{.c4*w4ʛe&",hz Ύn K&F[Ml逺^(`n=?K\80d =䶹yL}7Y&\57 .1-e9dAV,ͺE`5+wxU.X^[&E_Tñ)rɬJu0Gճ=NAؚamtbl,43ELPp}u } ûW6K"~Cf¤XL1!'HyCْ0Lчyc{cLp'%M|NW-oI `ʌ`c]ͰV'WMϹ#Ng?opuW{ʹ-nL_*qyRQ!m'yz*.$| F7 L 7Hd${/I1K+ Pfۋמ8rua"ZilTgN `r3G:YIY ij mEe׼`Yz6sUlK~n' 5g βo cDWL*ئ?S`PGA<.XDoF7CRXS<= Ŭ{%/6On$xu{얏F5X=uS;ydLhR]t,Q,yfw3>sWt?tomM6Y`;kQwtޞ0H>WEY>}i\Gxt@B rUT|T:؊XYMI0Wcfh0'U/%>jX7>Pt%0txզaL׉z\>(e:zh&>nbɞe$~f~!PXyzDhg^_w$އi./*x{Z4t*ZZϭuxgB0#w'ny,H8>ÐPTV`܂#_,;a`+і 3~ 2y\OFU]|./'s'<<41lqVTÍ"B7"~6 Gn=xz: |-A&pL^@8dٯ=XhzAx=7.r R=85o? 4Z‮r?jЙoݤ|FjOk֎ŘK┨܀KS1P?AyOh0^+2ja~bR %:$Xl~+ŷ! 0h,Y/HVRPIi﫭÷"ZV/Lc(LHHQT RjP4YRd좊A MxlwkSF;&t)'*~2Xܼ37 s72 ni4.roH PB'5?Og܄lSa|.4(+-,V8LҚwpL2s it_2=e֔|jzIӗa3^ BjYgfDLeB1w jŪ|Ct_{8()Vd=Ni#$R ̆bk.Esa@&L8j ? 7mu}O xܗTdQf} #CBKr2=^Y7U49jC"7kk1Ttf yNd/SӻY؇4܂~|?aݏ(g h3$d}Ƚ ^e 4 x6h{5$2/ƹf ǔ$g:%~6,iCe/RLu~DKov!WEKgʔmR Kr`$Uj^ľRJf\  а|1G99XoZXzL?UsؾM OPEeAɡ!k9;*r{_ػod$h?H%[k9r^fmZ˗I pV緀ix{Ҷ8)26wi9_-:O3qP3wXSy:a`Yko'a|= N~1=}5R }]6tHf 8.[&$vu+f ÙdؿֲC F 'PqZ7D ζ8n KBߔydnw )$wI3m%ֆ0SkС|xC`tla1O4lMݡ{b!/FC}uXscTL;i=AH> } rvzY{(̃!kƑgWә+ +EagTb<,‚dSUC:l7nv) YeT~xwqeH6 gZCif?$1-A,I_pצGz5s#^9oC2[Ua*˻դŀGLX݇}XrS); IqMKTM4sIXI'vPr*Q44m]-e\pzc2ҋs~t2 eZ}(CB i-I=1G#Ћ2%,* ]PEAwCHٮC-\ߪ@Na+0h_ lTQ4l- F6T\yz Cy3|NcECo|3Ѿ9Mb6nyT!K (EzLXSa~)ᄒz>0~Ś+l\-", 9^B4gو, B6IoBӹ1j5;E'ǑCzF'%ܶg..6ثI.Ui ߉"g,5kƁ*ܾg2T[%Orr:qoww 5;XM-L>f|h?<`3=)9a⹂濃#u:XZH]D:m"1vyqY9J{"C%+&o2&R{~*rCJUb 5 {XlmJ^Fͬ1 ǡVXF4wy7w?75u{Ida;X5s`fO~Ȱ0ϨTzc,TBQHHan)v|zAE[\Hfiu pM]:`"%UƟ+9c2t'h2'9Sv_g] ?o{344/YsjbQZQfpzbM̛ՑWRl3e2݉1ӊk˼RT~ݨ ^ rb((HFԸ{G^Fu5Gz}4X#EXIͿX3q`H_Z)xZ+燰?^ ܶNl@$hZKG* tejbrtq=T>h7DhadLn)g :<6'|녈$n^wϽ?n#+=6e_k8nj{z>ự3FX=߁(B/J'r^(?%>7,'0=tIr/&YJh*E:Rl)@Y>L럁1hvDŽmks%q`@+`ܐl87^0^N。 ƬRvPxA6'ե\kȆiݘ2F%53M>_W{12h3Mid!nP{,´h@l蝮q@Br-㍣)m2lGR0LDXN)FQ.DRX^?XZ$wS&2i8 )WD-@z9,QW*C i>-\Qq! h5r&J]JVO 䂛ȳ^7 Ҫ6]-\e|$ ͓jeopLh 3j\_حU7-9m_H,`$qP¾NlV|M]tq/r\^A9 8ەyF3A0gֽUFGwNGFɘ!@W n[ɡ {ŕn,麗ղӮ`.Qw.1VMy'E8s!lJdF&9{&՜'Өh^<VjV| ^aB5 5~jt`ǭL$QYדcm2y´R 0׀dfuqI/b}L'_M \# :i+ V{]PICP)K%шj:,CN qqDp̃Vݚl|Q΂EUEKJ-ۊ8v)GI bl6(1po UDAENS$ShyB#yd eS:TY_rΌ8 NDaT2gf1p,{Q(58}4>[c_a_p㳇oKsmTvjvDvo. [yG8z QfL :>aLTU27E,V[i>qTź[V/5Y&t01deb{δh*̢4NZ F>*PWmakKˉOT+4 *Ok:7XPRPЉ":bVJăGBA+NG*@&y*zYpu/2-]!+k3}1^K?HVyYMP߽1m>Ƶ=v gj\"2Yl6`Z+T1XAE!3,x"]`j}ܳR}\REvs8˗x>_ىkn"GJJCe;2EoC$3V|RWN&F@sʋ"][Hw9&|;:2 * R%G`biPXcq*%J!8QL#JѪ|}opE("/x.gRM]FvԤ"E5fN~.>dAB zvxiLZ$m$3юʖ`E/Lv.ˠKGDŽژgwHijhdq(/r( K}Zķvl- I={I m"Muf&ʍY HBfG(IGqL:HQs7W^t ;EiN$bWGT$'ټt<6NBj}7VҐ-lICKq Qt\k$\\㏐H-r[Zko{IZ)UN 3PZ)>fͯzNV4-/>í|z K47}-{HOpoej*w1* rhUŁlzʑИsS#+pPWO`?hmhX`ׯ۸c;8.+aK!}\h,6qXV%/ԩ}.¬Н@|L' ~~+b7f{wq/KL0<@="ȻW/߄8!KP!JB)NYK֒bgPƒd$u^#wLiT2ND;UZ4KscPuᗔML˕5dm%s,{ >^tD  b;D&'~Í&Ь:+M(/dXBǮu9}4W:F^Ւ)u?_.K$eQ@gr?4}tUЩ]f)Y8j&'KphRSx1t3G߼Q.({۴JbSҠS& 7o^O;jV0s1qMdGrY{D,oZl[[|\w'V~ G#[('O(O9K Eg 75g8LML%V v}$r'6z׷z~Ve,uB8iӪtow-BoA:֣?ݷMߎU;u96"9O@GW7|y/`G>'4̴`)GȤ"gh Ky14ΤWӬl%ZSkށz#jAWT;ۅܫ0[-#%/j0U 9/ʫ4USI>(&{*~i4@@ OW׼ٹD^'ޜ~{$5 Q2NO_΁mOOvיbɼV݌sW77o6A-zf]]E%sIJ7F1.'L.,#.vK֙؜x=RJSnVSI&yëHe-$\ ?1*5ѕzI][dRW~RC36:,^j-WΪP Fs}P1»PLv6#'5YgIY3*<0&*cˋ9Ns5/OʅsMJF” ǻ 4[J]`d!(D5kUtg<_á5)\Ziç\o?T%~MQDz1) TYAu*`֊*Sw=|1Nԙ`ד7͘@x;o\c|k+3%9vߙJ$pfQr`ԟ<(GCbC:)X0^dAKF[PvS!"۽M.*ƟD5M 3H;s{)p˵q*fƢy xFN'&UB`܄%zI-r!(?ô9^lڛ1RGۑԣ_wfP%,rV)o5)MɎ E <6ϴzǣ~`Γ)=yQ#s򅌩Tꍸuv;Ǖ\rk#+C!'$Y_PE`rȑ!2AUDryA&%VGfsx{ߪ#J5״6A.P{7t6xiD/[̓#tAN˳.w v₌ё/ӷoEx?H{lar{JZHij{Y!!o:=(&ET[{3PuwۛMd`^'C'<z&IiYڎW) u4UP\(uXY{P04i":K9B TͥJ3>"~[Z~b4pA6g>ۜON Q N0 ᭬ͩ@!2OnLeB?EkA$-7 ? խ NDg?4[+o8Π1o<=u'}6m k/Wc `RߔGM0atշ;g0p.naB G [>)?`+ӁUeѴUZ>­ h^ޖ2䙜 ZGaF_a<ꠐ%EV/yC?ՕMb;cqu4"ywx%=!7άi<^p_9]J7(a6>-];òD;Dž;H2WcbWTF^SuwQ-"P=$Bu}Nh+c8{oݙ 5~(#g't- ry zqwn+3_yy ;KolO6j;#'Fف{=d81tyB(9 5o= ?'hXGɃJ*y"\ʩq^j}PInVJɬ*-[ѳ/fҥٽ23$ bq?`P=vDI5QasÀݣG"" /[+\~G<eK %n^4tvSi`ڼyflivtjI.akIJu,%ɼi{;Kw 0"ܬ^T*J"ҾKcoNCR8E=m/U%@AxK;֚ nc[*Xri;hk<2#ntנp%R/Ǒ-1׏3+]`϶j?u`&DOouvg_8T] 9lwdD|RBEwNys+7p7>˰!P`IYFwUvԀU"tle-bq >P[I;͜cٚ*@{ɖbʰߗN ѤA֮N3Y7Z Kh &KA/oAؓFufHOf^OƱ.M^"qJݹ78ϛy &Q^68D fECj,q@BHp2 7hL$8"A)CdlL@箦[5~ S &oprn4tײ:MCywrݭnXa %zF re VIзgh>_H oE%B&=Aʘ8Iy"X^%ܪfsUi2+\h֎lîby"_`: ťVy?Ȟ_orp,lw5Ǩot0|6~#!+FBK~|ATc> \6Jn1:EQ;(RmH%'ZB Xs5WsTbZo q\O d>. lC ۖӿw5 Zgu`7RYd NL8ү㚟PQқ{z'ĬWz>H1(a;xVs‰!rOJNq4-1Yr"K ӟ%zGYHICjzW 3buzXL2z_ΕZ)gLJ_$9:fSl#ʆ-' d V̷OQ ޺iGK6t.So)xPXf9,5q 7HS>oh p}G$)XD*fFw:(~6\LNLadQr %7ػ]?P7=˝E|. Gyo:ӂ78213B|g9 QEЪ*/gu\HN^A?*ۙ&/!9+r< H.WʺRk4|b1?En]$((yWz QpHa1rϾ=Zr8'kz1 W<~܌Cj"7;tkeyĨb!POSp9.ÏlLJ5nS%(kqT#5c3:CŹ(3'F({oV2{J:+,9.0X\z>ˡZ8_ ~3zG&wyb]T5G_O~d>pֽ]'JVK1JuUX^J[X&}s+ .:|Qx*H0g@sH g>g\l\܉-h#CG{_ASgj4FݑEh~6Tz?d"E:8GSY-xp)xM萙wdnsv}gI= GD<e5vn ih?kg!rm~[XF)N1f9$ ꦘ#X;ȇO#ih̑:j.NK= ]1S, uގU U1j' DUycnV湗`.uOǹ*ig(AK*P4^bs2™]?0}2룚ai]'Yol:=S9k2p^zmG&}Tp=C>|cI9hy:rŏJaMhV*QO[8"1y +%)ޟT~sF.t.J<[ $_%0J/;Zj팬(O)!;DV~gELO.V8fޟن(VƮfr㞡q#x=xhI_o.ecH" WJ9nqBߢYpNR\|/ik^)5ִwt,a3jqgR_>}+b4PW\]iRɺk_D*M7H]KSSc,a`_9>N> k*%5Q/̪ިTQyYv`L5.` ً;MrլzNQ>d e& |UQuZjwKRU(XV3+j*h`Z9L+ɧy6[ 3~o)g$[ $crȊR;f?'hZ+M7*IGDI{cRB ~-1}?Cd~1쑸epߒ]0T sD6mm٣+);! k~Q~W.UCCH,€'b ^-QBSnhi_;oӷ!ϰԗ5^!)E굥tSӤx(H$C[(٩P f$f /R6T_n^;H%swӷz V#n<<+>:T"!_8ى)*Cᔚs?"Az-b"Zơb %нRoS ֦ez)0|v7Zae SL SԀ8e94G}h$šoWXwa >&d!}@,ġ@BNޣV#%2K j=; eyh*oMiů#ۅ\ C 'R{4zeT);uu%23V7vy?]Z/U3\Xou1ҢIpK"f|P.AY՞U:S{N$gٝC#'X9L-@`%7/k~^a^D[Ai+|'#k75^Z#U[m?]5Ӆϲi{krkK(Ϯlv3;䠡-~vc#p ..|DDƤYk#^ɿTk>9<0Q'[B`m30$htNi\8޵l4VERxS_ܲ}rXE &ݺ+QC1IaVS=v8Վ-LaXX Eɣ8v>IwG-äx\bIs*  ;e$z8S38u;!Z|.oO[3L戀ZG>Up8IU{!m=94)s- _i _TQbݰ &/AʈgēX Di!:Ħ{w~&R/;u,9,4 t %[x WQX֧ Q}*fVv$߶QW^PDgeCJmœ%13aߕH|ڑtrjAPC"hV7f'Dû=4V<롲b>㰣t=,TqkӚɸq0(NhZ wgŸ\P%F-u.1Eo`h]3k\Q|+`2X(Ls/(ɶ:mϟ%d׀Va$˅h=anܸLj[Z٠lS /e^:T >D:J8m 5+iX}Je`NW01% pz?I> xBPMDdYgޫ`1r ŠmF|e?N* XQ@SX<ͨ.XZ&7[ly F~wֱEw7Jstˡ3(Vf6'V`NŅ%Dc…{kmjɪ3ypڎ&Z*sM Me!VkF|!ZllI{[;k^'hn=n{he΅ =P 1'j3܂HRwXw0ǿqT;G+*3U,P㓣ǏoY;eAp'aemx&Bz /UaJ[!FÃa3S\;m~pO6z<7;8PU:"{qRIp͓4r( 3aA sIF;~[Z}eŕ"2CGi,*gcyZ->9[г!0`{ޕ€O"_$X~=_E ƹ&0/͵wl6wvlKaQ6—8Y;a1'óhaHq?r J^uסm|_T9~W*|Gb9^I̩:xih.+V NJD{?eZ%-*\Ω9Ĝ<}rK+Eݤ "+$L{;[Ei9#KK}>Q+'ig'r li?H6)zQ(j~= j-~%WPm0N,;7YwV+m@Џ)p*ؼA\/* m[}JJRH@18)$G:t p,ًp[҅/j;H5ḍ aF CţMz0Ǔ{$J2t™Z2rV4dT=ɪy- 3V#0mPhUOLɭc iRV[%@;:PM%4ji1"tVrڴײ' 62SE(Yk{ KMo31\ KS4SbpEDӠB1?i>>x:bGA Jw4ecL }Ơklyc&J=;5B'>*Fv,\#7E. dUJn)_tgeg&+%K2n9c3-G2ki0`NBq$6͸>- U/Ё e)`J x%p~ eDxH+eN0%RL6ojj:>T FT ZZ.QNgԾ#{ 8F xR櫭=ZA2KXy C\OA+u"N"㇤Y:i1r +d)4Q9AT-ynF3IwC7l]x 'G 2P 戶b?ewb LӖPi፛ q]E$eC~&ff"ZWM9ψo1z2I=l 𘝹.x]_/1][^/E8Qe"@" J&<*5ξQ3 )!fb}@ۜ##-?!ӈ lŞX50@"L#][u]>#l(0 ={"i{; ն}XVc<_l?#H T\gptZX$%a.F^1dX015qi:wj!J}‘lNlқr-xrG91ޕl *w&Aՙ 58e^_ \ϞF08RrPEgTD ukƓ=DnC>ۓ*&W,Yޠx\#_sGE8w83ܜkQ@.9l_ś9rz, y7y󄎹1NlUOk]{*9k+Glnc RAa5u?3uuAG-c̗\7`$S :Z-۠qFGa#"L$&J4F=`Jup"sn볞 ol5HQh4ӰiV)ɸaQggweF]+cegOJq<~K UW\%(\&"w22TE67zg2Ӡz {{;eԘzM/r8_ MYS*?4sK@'ltB',#tgGSf֮p ^]h6i 5%ZʕHMW/ˢR&=; A ФQ6wAK'uLAf^}JWV"`?=@"`֚t@ei]tye{GaPr$ojjx}%giDfi+n4TSQ2!s՜7 II/B!®74ǰA<=Ϭލ=H/1SFH4Wqq$ !iYtcۮLt'`-r^1eJ"{Af13L-h(`Q #:?_ԸZQblLNIwEh4"]QL5G4 L5e"/mtNcSy <.XZC߱VWb%k/FW鳐e·In)ZSܿ3VXdˠʹ' P]GJEzt}48)&DcLOWU8푋aa,P޶+2Mц9rijD#6`0'FC{ߠb{Xsx T\ ,b/|ǹl ]7mci Ϳ:[ZcPV*ڴTP) B}TѲ y*8')7v Qܨ ma!͜ wXz*q{ +@tQB;-"WMy73~QW73A/o#cx1Ϝ&\0ǓaحgTaLے[Lo Ԁ_bsЗ'֟76{I,k=xֽTF L\̽0*1w0O%K'-4*og:od 3+]3+]h@xs$Z3-^'93IЫUGg7[XQAW9 \ίӬpUXb@E<^O!Deo@aeQq%Ie0SΡq5FNюyPO˽hفw%.=4╸*ʎPY{kTVpVR)O-tKgo"vNc̱D\I&(Oלba_-1q?j2Q. sB .vJzk-$鐢;Y1Hl Ⱥ7؊Ǭo9;# zgL3 v*oCء)཈`K ՘"IiI*_KNkN=2 u+J'9,@f E98'.ZG4ÊS "ܶ]u 1Lx^&%Ka lIʖum(xjQYkWLGǮûkG-EFSя  by닌W"bg]߬@ILW'զ\0tI$AVDx"E4xsfBG H/b/EGb{ !h!&{f:QIPjʏADDF׃OGT󥽻V+mHouyjszZ'Z*soZϖ{?:=?NCy W!oIF%XԄh3eM*8ӸDzW=ԝݤ⭣8뎈.wWcnzq*ѸYTjP37Sd|"Պvs)`RDDL|7pe@OD۔5THpb-$'Fs -Ho$GWS' CǮǫeȟaDNi>n="#'&ܙV! שO* =mcW~IxÒ%"ؖ(Ubå{vQfʳs-}~ Yn8v f<¾'L $IBS"\{<3~AA>ҘeߺCWř4$_HuNWQIpZ:isNlmtgf㞛]&ζ:hcC}3 26wBw3OtUs{:ѩupxW\|;lʭ 1d(q F9;~>(ooCPEj\"R8Q37OԆ3 Jg A9`<³s)]cb3c0̠WX`]kjVjֆֲAWpt}'?1c3؇0#18rσ[D%Ds.Do"B=T'"s jZ7 X|rH- mZO;Oz(ox wb(X54f/1ce7:ۤy`>ȵrp'C{Yfq&R.[]O]"\0kuWdԯO!)X"ERɾx/7 JXa.X3N2JZ%D+h}VISUz:4hPC+<[b|}^sQm`+7|ʤ|1*3K Tum)v:_r7^XX9*H&\OAP RL3kD!&sSJ84r=ns,[iZb&-F||ԫDc =:Q[J!Z* {%]dy@6H"5O0XmYT5 rZYd.Cҁ3$CXp()[`S.n穂K",!cJ6x Usο8M ΌbsN J6N uy|6B +jNUCzFt{<ʸ&w !Q'X׭W6ߚJUW8nUɺEQH(D(j0F|r~D0exqUȭxI,ҋ.Þ< nϫ;S#CP2I󭸒X^6:5:)l2~Ka&2.8sݪ+!1+_p?[ ]xI1/%b@!rn(}b3zlD,̻J;UQwEM޺IVbeF!q0 5r3F#\o[<.xJ*'(pkPOv{Euk"+SrN̐ķMni_fR i -  TK  v([>f. 2O7.A"8oShI? {v %QD6wg8[JI]jtE>oڞvZݗ˰WBՃE KϺpq$H\gYm]Ǯ NDԴK4̵)&Yn"l|`sAsжԍ.͝i^9 %bw. #Jeb_$1@=gSPjD uV6k{ʃ'?'`t˨N^a T~2&z+`AZbez~ٽoQl9Њ.ʪ[pP~ފ. )'hb' Jb(:޺e|Fc-:{G.s v(}Vhnё߸-߰q۲Oj M+lH' :9XÈuSY'?v $U戽lY9L7W$pBVVhpzxeSp'sbvC } Ѿm@% 4**"ە3‰,Nz-~ϊVt6K:BGJU@DRk:|MwQVjx* @(ϳxwXv+=iq/z!jJc(.gitدxQpY]E%}*F$h:4#CSN${=O~DV} @1F&u5QIx(3K}+HbyWOOAZt &E$7p2ܠW3~3p"0}bl$g?u*tX&~m@dqc mw 85Mkq,/X;Kc-XaVH% e9dXcg Y&N]8+aAm@|VH]2]5‡u0M4_< W̏2h݆/pm|Bki:BFhkOdLcK#"@dK([+K^NoȮuDX۳jL퟼C:kƀ֝d lFad/tj}~ce(F&}NG) XH͘KPƩҧ7ͳZk&~XE8@?<}\ &J7ʸYy ??;BY嫯 <4m0pKW= D3rn*269{Sz׫4œq m\(K9<0 0kkDa|ҠK);ٚy5~%\ja٭CEHi<^bIL[CzP&t.2oǍwǼI7X(0+H 8{*_'qAA#ŏPk!C.NluC5Rݥ&Հg8b/kб}1Cž'Yf QexuKHC#4*O^0NKەPhX.h&׃);qRǴh6(y^_Ю C!d,v_y:ħXjF&8`0MBfAF&q|"0@5,f ڛ~3t;$7YӀVq%k!8ߧZ r6c/-0r}mmk,?-IT H+Ȧ?%COj3VAm+RZ&'C 3&MoT(,"W)zj?D9 oUGf[a _5Cbc"Z=%X~j cc@rk3ߪգ51oM&d)( s'ѩswY;":sR~0Mz%uk2Ӈ9R׎d@,R Ad ;D,1A #&jȓӡd (u"UW7v71d c{O͆d_}BD.m{uWrgD%lH}>=bϣiGCZTL&QJeҺȁBJ+;L1HbC$\LGE01g: EEL(*fϪk @(w3C+GݸYapB J>rH%=0 aT >E'iI3U)z[*l48Q7_"pt*k. Jo; VB1G8 Fプ3+d#Ck WAQ+W]b>4P{fG"84;ګtQ $9t(eoXE%7 0M.Ι<.~"3{*XRo-moyu ZwQA4=@'g0*ZQ1ЧyFBf_mFBbQ}}>Nk+!KǦk EKssM+cyfBE5p T(-Ϙ!A/Kۅ3CLTω!Aqx5ª>g[Ύdfc@U$16MG`UzG;EGK5s{YhQi:W_!ґ!R`:;.#vW}QҐ:bbrX'Y~"l3@ɋ] s-VuL.>˾v_X\¾czP%q(p6uc4qhfYPVj;13\ҩ [$Kci@x%R#JrzcsxY=~.K={ie0@* kB'IN.ĉ!yEG7AZN~HzKNB$hdcVNHMfL£f/swU( .uWW =2OȤ{;IS7p ZLS:-fAYg.Tv^" ]pp89W(33Bn=5Gn~ >v,Rdr*,bB{9cцY$iչ^xZ9 yknxx۩זLR2-xjL(U?ac{f/p:nq`Lr> y A؆X!ݴ柄q;heo\el3=^r`QiH|GH G i,ԯM, TO};0o\c%l¤_lX!4ʫ-D(Uw@,1gϣ*yaE:]!puLdx7#*%=K)MG+=`\.ᇵ3}'UA[nC< 2l՘ѠN舰Lg[P5xCHq"Ry-gCC> (:;Жh,ީ3(]Lww1 .3ޕ!:4<؎Ohv:2|E86/>*V9dٜon3' {ؤYVu&\f>? =I̕[:xk/Pn!y^OR`3m*=W&SmLMΛ3?[gnDM "!m]ŕ|V.zAX9pPWg|nlBJ"e-<);Gnk)'V}/؆Eމ UT /Gq:crxeBhYhn`ɑ%Ɗp.6 6xYZf97= MYh>>lzHBM$0H;.lpuIZX\N^D,gRj)T_a>Xܗ S-Qk"{(}2^DV kC><;a<*DRnQ6Y["k݂X;W"L{U]8Tľ+Tg8tRw#>8Q;x\Z͖93H} !?!#N] B '/'+m?~|(_H5JaX3g`ƴԫݧ@,R\}DKA ‰'dLj?>36fwcSܟCO Ҟ1=qlq,ֱDS]9M\)ۋF:#t;\@OBfd } 2x^^"qy?vU.W+h7 =AQv(qzy:84Q)fN>q8Ad]g.VfeQ ̛Mj@bVz)j7=Z^2c ʼnL]ap[}+\TpkUQ+6zpTf>JZdfw4<o6q^w\5al{PĄr%Ċ,wG ([2ZDm˱yCui5=A;Cر)\ۻ-$/[`M8|CuQ)e5&THv㟴{Фȶs~u&mcOVVHw/b9^xlu-8V,UQ(~ͪigm.ezg"j",hN2۾4RpV; j\Ch1(]X\8lJ R(O }MD:4q"'z L}qIRLѠ{gc7u=Ax=n=yţpzF;EdL>uN#y0q@ % ޾Pƭ*xp _vNk^8:O$'J7ԙΪ`<Էmqǟp 02=;z|f'a2!\hvx&Ix > qlRG;Cv7„HNb`0sP{HqB0}P@I P[m*W粠t9NriR-߻]lk{;`vShOڷ|zX!qsXXepd&gK$8Ϋ({b)qŧn`Pk;2q\u[oNhR[?V(L@Eb&Z3D?v=HUJk I~oV`Fc:Snu^D,ft]Rr8+CSP (EV3Ѯt:+7Uj9qwBfMaZ%/W絈#[TG =cNZaf2`QK/6࣠!X_mik9C{zܣ)5`g;iдSTU!bRu`f<ތGNzT#OʕQs$N oARɭjː?$,BLdlx疳 =ޜodJ3[<?̣`) HSjttWQRD+H:\BQ\}aI<. m!5%|y+bHYfðuKCQY+Yd-V9tCa1>f NMKa;©WL;G$~"l YkL>;Z 2Hi]]{i_EșrfpYK/1uT<8i,;Ef; wv:k' lӅңF +hj6]8ĬiF,V#P"?kPs)u.,cK@Ur|9${>!ޘFD:s;WV1qci^›|P;&u\}҉sXt sdEm,q(6CѶّ}8c>Jó?Z1ak >p JvUA=ѹ9vx8Nꥯ` _! }%p-41%?#- !>;7ȚFgbn8‰X&5 V2xjIΊ); 9Sb [POlU<|:[5_Hq ^}0|m!"4fT 35 [ &Htõ\pR Lr)}IOg;N>׭wZScXϊ~A $>ڳiGu:\ֱH QH7~T/un< W()z$*Pm-rUMk*3o5d"}0T?%J4Xh$ )"z46h> C`MVsWpv}"#Ĭ5QXfLu\̾^#s6'.1ϱRh"^H.g8(4a#k+:TR Xqkc] K3ֈ0ltoj|Z.?53{9?sR^"K1Im]3W!{)\b_m *-uA%>r `IKh]:0JY=(bIF1^v|V}l&--۔*  LAK"gZ\/uhEғ.RQU jsj,$ِhv6l$>"C aH9c3q n[,\ņ: &[A2km4P .o[aUǓ'f[Aї2XF AlГ~j&"o)lwfj0@>}5MrFxÊW6P[[sQϠ.iAxmwr(Yy? EV}JT/U!/?TH ,PnV<}JCNĶAp.4r d7qg-  !F9VUXOhDr@1r"]* S'弨cl֩4#SÐ]'{SoeO/d,_3ѕ[ aGzWKҧPOIG VVe% zMA|w,0 0"mcvYT. W$;swϹs)S7?g̐&[m{s4@b/mkCj-s .M$=5[ G6Ry|*xT)鑥XuQ[2bF`X CS7Sysv|',GycBu `Xhd^MׂX-ʸP(A]A "R;! 7h5b>@%Ѓ|ZcO'm } ;]곝ޚɼƼߔ`>qi\sͪԘ/2p3>/ߓ Bu#?浧EK^0oȶM7 6,&nc  |V7"ZsrBF]HF8E%S އoVx d2zA4|H돹K@C~RDub Gp!1ZNKo-KjaMC\1d\f4fc=,ǣk{puCݧ$8{dKtoǁCq9 ^ɕj5 kf8}/OHA_GJ=z} 4?q ljS +Fq om>7tW!d=~LHݮu(|e/D.F OUQ$D&R$'c[ZšᡀYSD{?Y䫸UY]cSFhS8e9='^`R<&lO|ZjH[ tc-dsjl}F߸ڇ>ee0 u(T2UFT^~1M״ri@ 2w2;{Ćvb KTs`;S$}M #ySc3| g~;A|nN"8蓵j~5"ӳ'B=.m"fcbD u+ظr/Gx*8Sc`HF-K$!g_ io- ͝UEH#y'=OO5ڷ  ̸(=m\ YM;x'j`gSv2]7(cBJAZXcNi.P"e~*7,*Q0-ZxI 545Rȧ^ ތ?{9i-cČ6]|4L-uȵITF?O,`<pog4HpSIJϪ*2 +6~_Y!EA&1R~ќQ^.ʵ!=\":Ě=n9Hэ Ӹ$Z;g}g EamT{ & ){_8 SvMnU)%9ZMy`b+++LIlK~.$%ʛ8ĸcL^]L  >ւT|?W|Wz\٦jex"ݶh˩F9;gpCmS Ky9O @|np /ƽ(؅Y{]ס:u8e*yZAooq˕!;R<ȞzZ?,h, EOӺVtu+1d3[YLSM^PC'v>'44=qPhf?0Xl`64|:iXpзw}88p _جYRX >=4d*B=K_Lb ,Mb1a6& BKx^4u "cN'RvrpFv!"rAe nX继fke{1} G_UڛoCabP 6zޱc2Hϊ?R>]@;39?|_.x34L/7lV 3 K 7Sjq _gpa>UD~S8hhoӓlp=QwU9UӬ٬xg7ekjFUjIHŨ$(I`P`"}=o fHAuYu? Iggvy(62'&3[r1sw^Rj9-5ΉL)2 \q-]L3WE;W HxbQ[эA§bJ~:4w.S@Aשr#.bbjjEVЏ}mKD #nYfOfKgL?$b˶ f5bz- Fs^84Z])?^":g Y9 {)̿+_H ִnB> C9Šd`l>*OccUJ["}(Y|LQ#=p/H?&).19 ޲C%$t!bf1cĩ[[M O|rw@jo;tI!t3. cȬš}/Ah!\LLּy,#ŀ~Sz>Gy qjeԯٸ(+m DԅG-3R^odTc| B|$3OGd*aqrnK|9ܥX| vOzOu3wpHJКs'HK_`VqI6.:/E%%ץH\25hG9>j.Lu (K86&*9躿MOPV8sQ>l]qo ٞ5ir/Q̔{N;.l6Pڑ2^b ba V B\"S}0*N*&0v "QĐW>`e~l $48e{RiF6Z!ؑ)Tkʢ oK!A(? e+izU!Uqp^7vM23 69V>{u.AGٳ)wMYyM{.5b ^:kuV|Qn0|)A!r-V]Iy (’b4V&G 5pp ҟdK x3ѫ҈֙Lwm{,ݻ{8 =iCRF@_}$a(*6yŐ &&T1fB&7~4N BhC XQ"^wғ$ 39ny^j)X#_~ހ+DG^W3ևQ7;W\  "2́^7)O H5^xhנ`HÃwK,buy?N,E2=_8z.vH:ԓ'9̋S]Gsw0CNf/wV"rދA ]lt h%VFgS,NJ//Cïp ջ2d%La8!BXɧd6gb-uG-1VY095[edH&AT:8(*䥢"\<O1YI&R,ỐgqGX*XNH sw_牬˲I$ڐt!;9g4?Jt70壉>ҀM$[GoehK i2ý̿g$m\:HAm~ljoF4T΃I J[ǰ6eJ˞o Џz*D}?= Doиu5޽BQYiEGפY MZ+v8 U]*ژ(:{`wϬM}`Tn"^dDA6hݶ ["[57V&W |oy+nqއC츇1pDݍ<`!z^:K(v `1Eq`@&M!T(t۰Ŋ!08+0KK>-p^':jҒO[\]iu<73)ڒZCڨi} z >9d9őbᜩ핇lZ~H>n^,IY" pR|sܮ<$Cn-Zc<_dWʸ3[ (A=_ ,OpWy\KEѕHV.*Q; ԻIw6Tku_=֌KB0zE#<k$P'VIg-mDQHqO Г2jҬTAW8Jя]vWu¯r @]Gr'R}J:;ʛừp+\$[f[&ݰ=p$/ 75c]#fbH*FzGsdxU X@ .XKw9rHmp/m|a jՖAzߌT' XGw͈O7)@'0g%E. jaNS Gc>麝R(RdHe EmЮJn X5v 0+h/~z86L8?yMDc:87c9x3E= hfѳG488dΒW#x4dk:AfVH5V~@NQJlM|sR@-­j5 OQ0 5sݱ>{:Ŀ2xۀ䎈uY=?&ZЦOAyB!c ,\9zlqL+Qd g"dT)巕+bmdTn9bc~ wIF)ˌ9Jk+*O渖sRlkR -[$= |S Prp\LWw+jߒC[piߨϚs3o"W >=:!<S(m.bN %t UMl`8NFP!"P?G2Rkw K"ʻi[.<"9 by2GPh/p\ZicSbCP\ tF ln`YJ4vyF>nG|4>"t ecf*9p ". 88!TF<өd@"VUmfqhO 6'eN1;߼UNM ))0ʴcT`N&}c0V-f 7tܴb[~ 4Fܢks#Q7hr⯿/e _+XZmq_f\Z!W~-iD:]=x |Jz|)$$SmdY Z$. qd&kޛ` Zi,峜-h[53(ּ~}$I_ ?H$;Fv%n͙ m;V9d ׊XDoydP'/99<)=pꐿηJ-S 47urD\|=)SSJ`F%3O6 "] G5 \Iҵ~.y[%[mVnZtQ}ڛş Ⓕ.s̓ 1$kbV'3{7ae鑯$aVimMՊ}*}ji%K@j\&E(J`,7RxUb'@[Œsv$>Ic(N&< #키cKl;!n/B5[)"q\yufԣro?ΊlͥOGӕi\I)W\.k>m[%gL}{T:1}rJ3AҲ"/`|~]jRr`Pt !,b7(䗥)( y[kpekOLp W \W6 6.|LQTl'1a_(P2^FBdLVh@e35Q_z`!G &܇ T}CW#I d0ro0My`(V:j=\@?$Ux %X&yxpL_5S4-uw3]KgVyդ'jAgﯚ2PWcAu̔>ev0';^2jWa-%L>;ܙVla#"D? !2,1p8)iZ04982^Iλ U_Nvb_1;(~)YTfEei=;Wz66WzWA]|2MGU c,8I j_HBNB2@;*1dnbKPP-[ZY\3(B}md6p5+DNO7ɓeZ!/Xln7*mWvBVFC/1@*c^Lk&Ypܧ9K þ7@[:Yx=XX$D &9ېosBፇe!ηsl&P6{G|wZ:IKEh࠿^i{\+ MT:8a)Aa2I]9;5Uv57`} ta iaPN_  9 ;1yRW(+G#l]V`%dO.ҳs&asu*)BX[Mm-i؈ګ~+/=bZ13 "ަOL=k~n@ۃnϓu!W`ЪvʬfvL BlR&:IO a$x /(C#yduj9 wt<MWb/;.gjOB@Te{I&e.g tB}Qvu떐Vhڻg:40<+LdJ+Nym_&A, R>L6S_M^c{>?}oѻ4`a;ن̝[q=?9>Yy r򣹥KߐYmK  ȧ ] =G1< FOU j WY ~a?KױN:G/ 5> R,ΨS.&pX\}s >7Sខɽ뒮~9h|B{$;.r"mc86kS=U8ZAn0q.f![.X7W|IJ//P\y>02Ρߑt"}GH><$o 9Y.X NOߔ*'֭0 Xb](E>F_TW-~g|sk*kZS.fI]DDeiC HzkͶBy]7rF<͛ta*jZ@F=pݪkE•G&n|+ 0LZI* ݏ pS|Ց} clLElK5f886D^-N6+ĘgN31CS_;%9 pi3rP#Uew_-'zB n˘RJGT 3';僪z!'>->ďGOrļnMlMܭ+c+V<n7ljΦtvO3kE$qEj@ F)yYFq)'ǹIȉJⰯ:Rv)*S o}!;O4Y1oȍ_$Z@pz@0|$SpY9cUޅx|v7ZRD9Kc_Ƕ?#We?W ^@~iG5m*^7eK tɰEJQ.)i\.2.l7"̢H{;@E,g'dl֒xW* Tkݱ|5I e"P;>r ~M҂*XMR(s)T em}j+sl^tq/ ɲ)8?oh7m8>xt'͘;ޅu@ʍA2ҍ  h}`w+wIQ/B תc4 (X%QaINg| `\Pf 5E2nVOZwZv&Q]wo3h!I0bzdek'Fv5Xt.ý{5> 8?,:Vq3Vs;zI$mP;|`|6q+}h{Ut\ S`f\gi\ইs951e3i7׆Xcn>'a w5 *d E~)lr$q9 Ɖ|'%S).ulk,]Ӑ|v@Y@KZh " !D%H\ܬm由.iϤ6wCGPMStKC'i9^C%n;aU]rǰh ;2l{ϻ8&a/A,@&;םq@5Pb6i8Y eAգ;ȅ`-}Cx"H0hkU( ti}kzqɥ[*Nd-i0Dd?vmE~ZԜHl8\#-,3V&}IN{}h~,FVCMhQ&IL;M E١`HhO̺쮂:J]/#3 <+dHJm6l'U~0⌆ys\r PԻ_&|"n )bkM~Ѐy5VA2ZhvNw`-i21O0 r'bX}hwo4Fk`o{A⻜u)e&ET]R0\cG`vvUo#h-+cq:B8e  90 jo,M- 9^iq;ܱa t"Vv-S1vKg~ P'1x?*U@lԚ°=Z 6m;1:BHob_.W7ql '#ͨ*2=,j#vaTka“Eᴊ`]sTm qLLzʻ0Оr\N9f>cN~wjEs|))۰]~ұqR"vf89x%eKRAV+Ky"⹝MOz)bNE#7z_6ղUotiacaMTgux~.Vq86ԊmЉN"z7@9IODyCk8Гg#ڏgnvƑYk>$vP@E`kh*>]BN^-&^"(%҃ݾ ~Cg]1(>N7@]PGܦ-ݺ]DZd?1'%b: =&>wA *M}bg t~ټv'm7㗽Xt5-:kRiY4-T1/\JA*lV /+ސ's*3oO^O'Qzs~k~ڣ"Ao-]1!K]cw>cztc-play-1.1/test/reduce_test_vol.sh000077500000000000000000000024571217612412400174400ustar00rootroot00000000000000#!/bin/sh ############################################################# # This script takes a volume file and reduces its effective # compressed size by creating a new zero-filled volume # and only copying over the header, hidden header and the # respective backup headers into the new volume. # # Since the new file consists mostly of zeros, it compresses # rather well. ############################################################# if [ $# -lt 2 ]; then echo "Usage: $0 " exit 1 fi SRC_VOL=$1 DST_VOL=$2 # Find the total size of the source volume, in bytes. SZ=`stat -c "%s" $SRC_VOL` # Find the total size of the source volume, in blocks. SZ_BLOCKS=`echo $SZ / 512 | bc` # Find the block at which the backup header area starts. BCK_HDR_START=`echo $SZ_BLOCKS - 256 | bc` # Define the sizes of the header and backup header areas, # in blocks. HDR_AREA_BLOCKS=256 BCK_HDR_AREA_BLOCKS=256 # Create new zero-filled volume with the same size as the # source volume. dd if=/dev/zero of=$DST_VOL bs=512 count=$SZ_BLOCKS # Copy over the header area. dd if=$SRC_VOL of=$DST_VOL bs=512 count=$HDR_AREA_BLOCKS \ conv=notrunc # Copy over the backup header area. dd if=$SRC_VOL of=$DST_VOL bs=512 count=$BCK_HDR_AREA_BLOCKS \ conv=notrunc seek=$BCK_HDR_START skip=$BCK_HDR_START tc-play-1.1/test/volumes/000077500000000000000000000000001217612412400153755ustar00rootroot00000000000000tc-play-1.1/test/volumes/test1.tc000066400000000000000001200000001217612412400167560ustar00rootroot00000000000000H3c(F žg3%CŗZcB~|{޹0PSִh77I+fNZ ƒ sj&>5uh/uRn̆`тBѥ!I=s0ҞYCO)p\h> -<׿,r}29LUZ=${!h9/D/?e D$|F՝2ɯ湼C;"ƴ4ӂn4 lҡ+Iuk~spf|#7r2Qځrg "ؒPW1AV'͸iCHh̅[0WZJh,XNk7ݤ1`f?[^o\Q LbgP)R1 0S7}J_"s\1R®)fq(!%NS F8xK*Zn<{>ՠ&C znm_7$*D.Ts&|ni6, |$H?ECvhR-o-w(trHI~_0W⥨!<%eB +Wn C[/zdO`ʖ͚ 5VzOT>ښOOCpIKnd*v%9=n9bNb126W/xb< 8xX(@ bڱɾ!y 8B iD M@Nxxp~"fDʛDF:y+U3^]jR|.l}@|I*%+=LBAZ0Zz[X7!H ]% \7!NXub.P-%1+ cMni( p5}#>5i?]֥sSJcBPR6m׮?)-s?8v7.TyUN3୬ 2Ba-ll:1_PIIrLKJe6QSzTɦc7%; R1׽W .d&5#Ft<:\¬iI(:A};hԜ9w5a%Mb s+R.LTfcR\˖6Mx)3"?NR&-j;sS :?`@ݕaWYS<^IOb9 N$h͒\ <sCY)qEаpJ!;7pV[g@3"=Bmc)ve. ԅFmE ȏ-Z^3 L`BeV)L=?=%VK+ia[~#Y zʤ-e{_\J]%Za){/KQ+nH7,U\ٯ>nD遘V FJq h ~\qp=Dž^t`ܫz6О<~NqsC.ĭj|2?˜k\՞VBw_Ǿ+h}V.L8!li`+l>(Uf.+)@|OؙiU|sC Nc1#,DmFi~V"́Jim]|6*"$i$ʖd"Q3;d^Eޘ*UAw6_ o]%.$!p5xWP[Bn 6Ro;:hmCdď$j0G+R~/ Zʗy+#)Nԓ4ej.&2Q1pp4 l//8O'm󤂯 Gb / M ew9T:x+聝mAfŸϾ C !pѮc/a;}eIUhZH -A1#*¿6 @M\Xޅ ?=uKA/!*eIAJ5, kC ZuEӘ/.G@i̙KN[)-X[nC:Ms•9 .;^?Tݓ巋#A\H Y.+7YX'[O-]'Ue1be p,r}0͓/X?tO:CWգjW8zǙ<<\⨞tT>K]e :U7-e2)Ş?1.zNnφxT?{OR/@?'\EC*qJ-L* y AFPl.]@w: ˴4P&"LY_CL(tctר= }r¤<,%_- pWQɟDzN]+=E~BT]0*ڻ("L+n׸G+}^Q>&,MGzȔi6|fѯqɇPdw78EH|A#HDd ';{M' Y}?|UeeJIOj5Dxa*Zo`Tcz!);$;<+&$i6N {Jt'g?1.dO^AI 0_мK[)@$e(J]G1WfsfHŏ1"|zFim;~M_MHbx Ei kwSw>Q 7h_PT|5j @>;:^JVbUQs"ßzc[bqOČDNp)e Wq 3:䒭9cF% i٢9D.xT|Ɛksxrw좧 "R^VLہd^fCUϧa%Wq"fp ʑ构r;O=OY|||]nYLp]YA@]},7) 4u(ܟ00o]M-@NKsOۢ!Y]j*Qٲwb"ّs gڒqyU-mu=pSa#"Ye! Qg eq4e., Y΃* >C$:fJ5Eԥd0 x5˹˳t\!J/8;P*‹e. N\h3YtNCou| 'C ŕKThn堩s@!Vn$ yǮHNx_m[$9vjrczn$@4 jB!&VG2dVtNN4g$US#>g銇0ږGsǗO׍7>ÆkN-Q颎\ew~Oz@r.nnڵ(J .=nY|زOT1L8.%U %T.S|̍ cPL_#_',bPxM#k _DW`pCzQ9p^iTZim+f|Ә\,WւHq`\ߤ'EE4;T$̀.z~ߢBE>7_3 QAa9R Ur2CmN`UwlCʵZ2@$t#IИpr)NXAT l'%VStĀERE~.o2AjFE4:OD$dDtYVҤɰgt2m|fk4&1wSURJp#%5:`QQ8 #P{c%%%bk4O pԛR,Mvϋ!jh3v?ڷX*]cER rS@Vc @wͼu{SOJo;`T,5PBK(yQ2xP*K]̍b C1?]X( 7~*-(^in'I3U8DN ](WUGrtlz] jUpZX.R3fp珖'o)?h |C}|I2#hI%i#ٻt ظ:!2Dxjܣ6%9gOf?42ZgX2ipLQey9E44HV xt@|$M d$)MR0MWI\ >b> Vup0'{Y {ٍj?ޜ] ƠDʹ?S)w6ޏxAG)" _9'u0h3ָ'X5G-/ HJ|osGX0MMGm&}}FIYE ]͹%Vʇ+?"\:*kiڞR$~q@zYh|E] %̓ eK]#~Tݶ =<]EXtiﱼȹ:nЫocѠ"Utzp`W*]R{g\ *0t4x3+}q=LʆEC[64iY!DF+_GFI(~CQTJZrLiחv7[6Yw܂>ZT^a>0<8y0 >2FPo"mx"|pGh:>,U밽BAFf:Rc$*>ޱʉ"$G4i9䍦HVARw.tv/ɦ^!AW~C*=G,5r2%[+ uY #7!,9Ju{Ўt`P3R "FRul YiX,xgT1givf]:P*!ZNlo$Eo=;*M ؜zdcoG 0/t, %fyu Ll ױu>J9%v@`C;i: r;tm0J kB/ގ9̭<Lmw y1aI@Pr9Eґg)7>}#kk?ywWJ*g"mTͅx\er]Xӟ/o`.{]o T'ʻ@|Jè+OfP2QF;4b k$)EX,|Dv47+1C`_ߖhIl`͍^^όLƠX=I[8zmJ*W 4hpsK! ѽvat.ڼE,jvOi@y^V,IRpdH)[5)^o Kk{W&6|:wOr>q)K'}s^#:1ߪuS=P4OA]lM$ iEuㄣ\{>:D^#ma6e3jI:~6tu>_{'"> [٠}eCk]5`iYӉ;NwxW cѝQ5X]$an+%>ƮH~yU5^QzԡJ'/*3٪q3c[K"- (o7{_&kKj 7G?p&:(~Xd8p۫_XhavZ2x(] OÆSqֹ@PKc<#4u~ΐ #K^wy "H=-"h_fOh90f vlmYBi,p'h|7W,'ڒsU.QUy?* .5AᢹNCT0--8y xFzÁ@wvwBi0 SԿwSE'GJkb< N2WdgJϟ?wKNtlH~,J$0ڥq9QNdot%N@Hjnfռv[{fG'AͧuN vb \(MAni;!`9B];εlS-Z(a;-uW=7){vQ.yn0~] GMeT9>qEG'rޟpUCYCkCOm7cekQ뢞Ƈ } ,oYfDyjهĹUH p3$Z'v(Ȋ,RmDs߁,ZZkx΀<.x[[hj$ 43GV,3/Tn ^4#I }orh3bo8o" 4ڥ HQ͐ EqS(MByapQrK0mlz^5nJ`${oK4!p/ȕk/$FSܔ:3EV"5tea6SreցVAd1BcklAr 5g 6чƜ+ECvɘ 5*Ӫ :9P0(ήS .iD!as{2r\ęZ>67QhQے/ǜ8HD u v]NQLtYO}V@\hRaO@]C2$[l`sNۊ~}O7 WczDR=J 2Ub#solsѦjF<g/|e}1ʿZjR 9| *)4†*8$K2FUܩekfз!#dخ)Xܥ#}aQ;y߈֑Ab:DW enM{i g5L0Z5ԕ?rX}& jRavڒe ųƺÆ|N,ypٮݒQ7NmAeתcDqlW þIJMg%FNB TA`Bh9Qi T:e᱋z$O_/~,w!#~0@i[2eq$L oB~pI 0Ϙy&&j5h[Z;[_E6Z1^Kӽvp[٩zIN 5߾\*UwCF26 >Tz tn ï{Ï$񖤣l{ {>q1X6x|&?W?j1\.zյ<$q= h2_oeW5f.ZU6iQP4`f fi*le†Vs$/XW +cIa&(&%`x|`iTS3. FO6ZۇzH l$a   ;l ag_Ҷ&5]ϳP%a.}̫3#J}GȋMhY{0v%6?5_* Yo.Rw8JÌ z +X=4Z4eZ]ۼ#KG # ~5~Y1-Ҟ]pǺ0*W?D>J(rK #.@˰(x#;J{Nr _|0f'3")b%BRD+59!z}1<I~XDIG%ipE Mj(Oӻ (~u1&P|H >wF: ZEx [ȿmu޿4n,~u`G'087pIްY3E¸ɳN?aۍ.,[@l3C`;R^R-k,7ޔRgԧdm3MG'̨%\$6c]7f/!T@> nH]pXgUdVC;=QXU"2A1ɦ-+BK_Pwf4ʬ,68VO> ƀ`Nǧ:+9ik q<:Hj zR)sO(Q3HnYm漇 +2wb sطW9B%5,VJuv.K?}aT&l%E(Zʣ/vjKZo}.i"?)>oY03k|P/wCNK{r4y6H\%缎 `"^90e[QB&k޴IuI;{%dd1(N̸((aC8fWֿjxc 7mRȖxvjaҚ;=Ul7MqzWy=`zB x\C sC"%;UK&ZؓƑ ;NCk`Ay-ID,%lw@:#7Pٗ>5G!CmF^|ĩ`;*-T"QL'R+ s/)6\rci] eX&ZY7LY=yn5ԢƍbL=YY_@Zfrk7{-$)bu/ļ@ԃޝBSh1:Ъ/eף!R^?H*m[YX~y;p/[ 4Q'Fخk @'ETN^Qo2BPsEPF?QBH|;f5;$}C,!ps0(L[6c:Lxr"lN0_S>(gwb8>WжCd` PKE@mżP6a㿠9Fڬ='#o_X.3oI3f:_@dxAU9yd,l+vtO*.乚z>OF;3 d ]i{Lx  ffT޺dн4>E2* N+iwtqnL45gq=x_\=([ߏWYiE) 8 YD,bxMI^|UdiglnĽ(f,c@>su*_ X(Iȷ>SE˶ugY$ 6CAbu:iX2T|ta#H1E"rҲ;lI|pH<e3A47~ʋSwD򅔾\5.|}Lt]l6dAAiah5:<j>ˈ h5Lo˿ 2ml^Dgr٦Z4| 69^ \N8v j5zi, 8CW~K:)=B-ܔ߸./m Iqj[7U/?p|Xk%gVdx;pϝ[$54BSzDprZŷh 2$t b}N[t:?Z^jࣹºVDžt F` _e)Cn{$%L+W(S=:3f|[T.Ijl+v GpRa~w :(,$HeH;&Rn:Ê.;]?J訉a8HW\  /<VoɛcSك'`!3*ÿ ѱ(I a9X¼]ZhǝLtj]ƋsN$|yzXo2tG[z-{j[5E1ػ-VkH^t7|J~>rs*YttnjuT,D BJYo|n >iK}GMSoʡ̈6 akZ^KQ]SMMq!5F<5 "|S B=u3y4y2<&E9x6`PƐ啽gVĪ[^D?N,M$TGOۖ$gxMomh`D;=`U'ڐB]ɿ1ru{zT-Ό>  `+:\(khPw>g&9-;}!v!BٶΩ%TWR")Ib:+[7o(%惗'7 8k4\|:ϧj[3s9y9GF㙎vC:}FCY7muU DK #WYdR##Wgu]1ldaL1z#k'yη1F.c}( } T@s>9]JLXcWXd,d^Mɮ]@r }O(@QDpqxqಙJ$J#زs$:> eOC찢;KusQ3!+c6ͶA_;R^6uq'+GƼWޙKnKp Qzp{H5@rdHk`nL oLJ0:xpSlFꡁ}ħ{;d2hRx"7kTwc4\j .jQR$ۂK?MbֳVxg;ρBe`%dGƵaG`cL]33~{">%]y%[i <;V@tao%:͘9ȇA `/Ծo ]4l%E|?v,Z;'74p, ;G5!_/n$r3!] 4D+6Dĩ;=֖ec*Fxk5ΐF<6j a3rɈ\"ckLcoxlaCH=>!:09ō a %DrZz}EA]D0xy+Z!z t,qڑr;v s;Hi"GJRTq\ʵ0v~F2l{FS'"0L:eR'L̞n9_3ѧ.o +l۟ VĠ)xO%{bZYE9tnCp )NS{FY&3_t'؜X,>!M%[9ztpxd%R>ok_nˇN0 %IJ 1g5ߜ7'rN 3D:|YE\΁MJ͘?R>EX!FY #S8vw+uHp^ӈIvgp'ڄoj ؟3Y@t ̩|1W`|5?Y.X47'c#6BZ;o`p)G?EU*P2Ǹ.h/qs?p=CH!b65 M$}zd ‚!7'-Ft5DFu+ěLCu!a.qiC%HB$=!O]kIGM:VU)>yh~ 1[o׋P*cNF2Rb.Ε[cI =.yUj5ۊxN]1?QtyqfcPGoAJ4B . WQfAص?\KUn{^$]`TD{Y:=KD5A o5{z-ߧSORH?Mk Z:ߺ?PԴA/~C;=YARQo k<ì%peU&p[BSYūJz-Qn"d-kf ǻ󊿱~:7W0+p;g*B?ONI$Ñ Y|3*l[ HM~E8?[ش„z`[2;=Ml>Bz%KBgg&~)U?) $"[*UDZ ORBoY}dĽT}u7 %F+S(u"lqE|Z+M zW6D=Z Q*ޜk]&|VdOCw~WWNDk~'9n |{&N(^Q}"2#Bc5$k_yV+>ODZϡ(YjO䂊Hd;bL o7#UGyu5 ACe3!D<xYxiP=Q]0?H=h6X]mc&9 {s3%AөW!X9,sv\TLn2Q#H42:a^?5-_tj"дK^ I&Wl |ɻ֩ ڰy[9BxEivXU^>D?SMQJ#A$,6*b$_91߀t.2_ o yQt:uGŴy$S^-W gHvh$9i_BBs?@== Í YKEX[@ᲊIy匝7(L( g!ɀ umI9I(@3G8*M/ͯO|g_AK/r8Y'+f |jJ ,mn"o3\KgJHT&L.>5 ի[| M?U4Lw%`-f1V7SM6sS?jdcŜ }|fuN^t9vnƐ&qCP"pk]}D4ܺ+zSSHX&~_$?KяoE{H eJ7^BNxy"^%F=-O4hSN`tt8G^S\Dr0kz)Z)'U5KUx5m}e_do4Td7RJVXtISSnK_鬺 'y'LH Sk,G!C2Ts[:8M\8EᢸixVXg|U] X#|gצ!0m4; U}IXh P}? |y 3DF*.E{8 1>*)WX *}?!)C.$XJv[1U{H"֪X#1)9gbV ᔮFE w_#0;!j"I KcRAQrtŠ@`>V˅Pq~H {~QQ4UHSTYWOn KYمfJ"O:,Άi[?dFey]Μw#u:5݂ z# w4F/ &IYd 7_ w K^V(!1!:'+>ƏZdq'_j Ǭn#g{hK q8u@9}|+A+}ą%uGiyL#I_kqR]oƉAĪH|2)eBkbz ᇞ'4b Ch 1]=3j9 cHZOP7Dfks v~DnHwyd6b)Mq%+ uzn *S45ǷO0Q57!s"~3ұp4t+&ĥwoVGmg rEVEMf\9 )@*m#ȃHRT6 @*HV`LByp\W T^e_W&vb pۂăL=n/NR壎u)^fۈWl.I E19pe1dV^!og쫇ډ5$?3t9BF]Y>++NL<:]o y  ٿhT?4ke–:[+hWI4qfR (M<]RoEABvu8)^), G[t1X`1ݎmhDŽIX74*]  CjhBCs&m:tq"I$n`XxR6A Г於-gzqV XEZdgVsGX'l}7u0Ї7-1ux  a>O!*S IyB^3`O~b17סk],DUfa$WUQRl@y를[N/MR J60Zgblk'yta&HY x 9BJ>x ]"CpEE0l:H]_1:~p۠TU;bs 9>s`;H+YFS)̙!з̻JKCy T 0.".(P)O3=Ut]!k/Ox>O|]t>@+Ra`N=.09R~~ڣ䦺9lٯ-vVÞ;_w NЎb,W 1oy}6Ü;FC[z+mꥈ+oMx_k]5>trfLEyĊ_x`k\b f20>"%$F:pL  Nfv"ڀTInA~KujSO5b%&Yu=8=]UZBZ*Rm4%H $\8S@t`JNP2f.5` *% P"6'ڑ|{ZfZœ} /Gh6&.U!Z.9 ;pkgO%3i1I|ǔ6Yw%a.Ca^zlgR.͸z!?{bx _y JURW/LB=´|!W*YVX?ԒS)xL^Qn 'l|s!%rc@ W0!Vig"eH;RhlqwA;͛[& "!@ǘҡj|Dcوh703G GMV0I> v9N˹6=N& os.R.c0B5AU?䒕FjEXR'$v^S HӺa}M؆^Vcj :JY{'SI"gneNq]9eH/#{{ DC%򑥉F)|s4:ܰok#OZי)VP͈ `*uh[=2[Ǡ^̽˦ nDzONWa&P|mɬNo}:ؕ-kTTJXxnk!WO^qºOl֍0 (R/B%@+c7[<+YDm/Ǥ +W|M9}+Wu4%6d('MQ1-7gߍSδyV4';OO\ Օ|AW*ƶ } ,HrVR\F츲(&Wa,Dbuy=vXE״Ҥ~^+*=X[p<^ q ;+w43ssqXL!c~V,a/b@zyIafPS:ͻ"xV\(>Ma9Lzh' +4)$Ć2NZ)]Ă~ 䠉!=o ȁ4ϚagW,[x>e9qPO"V41A$''`%%`6;eY9ZB!'s l#vGC;Zz?S3h O?j,CXpov)o.-Euc݆{g, ݁ z\:m( =Ϻ,#.i =/q"^ԑ502W:On:inυ!Owb`8Q{lX$ k!EY(^6bAN(G -#qF]ѣaGe*;cH܄.XwY":㛒X)O nYҌɦN[Wvk |._:M0?l&^UOrāKK9H-pz5vfjQadM/O&wE8p2>H^B}S~Vsŋ IL2Bx `;m +yAkv,O^ 04V6di:h.,@}h<'C&]DT~Usr?fm qgQ\@ OK'$x\WŚs/-%h׌*r y:IN#z7\3Tqt G`l2niuܝnX>KoW!0ɲd'c }5`fOq"=Rr-; qo) *]1%L-iHlԧ6pC !ɳPO7"3[J H1NY!8lbI /:Yᤠ+6ѼH6 0F.Vm[kA&8ydO|sr]|x`U܍DclY%Q҄ DjupPSaɖN " ?lbNX>Exؤ6~:~?:Y-8:H*N, G)!6:/L/+ټ~zԖ*fù%5Дŝ .}P!O8OݾY/o_f&s:I~&Ч`bh˽ V&=U> 2\xǓh[ ǿ%9 Ay ,T6ʶ!eI"~6 l͔Տ$K fXR-6yc^إ9X cz|GKןuPZx;i+bmeV+]MgKTx CǧTP%B40 v1\Õn+jɤIMre"ր}1rg9[,R\`>zNDGYGz">$vbш"G2i)秕@Zx@%=rhbvA !G{ȿ\Ery9-YSzPי3'àqG1q8|?ڛϫP5b9Yn$K|W'l2*rir'|>Ͽ+;nX5?p1oP GX3wP 5 )x5 q3C-zXEsn`y`Qv1lX-xH(,,g,} Ѷ1UsZjxCޕ=]04L9xrIO Nrtzr|?HhIbm0/jK ?,0Vp*Z >H_2blJv:wL;2Jؐ's|aM<3DT& xK(7(qѰrܰ,&{JD-3ڱ 9VNH2'\bK5 o=Y-ob/wO!hkO ì[S{ Ϟ\zr F XU_8Zd\i/>-ݥ}hmYN>ӌٜ2P2, 9@,E&a2du XSMR+Ucr/7fS~N^QJJV4vlxx~!Z$|dR ]к,"BP AS"ϳk=t&+8`Ԑ-AbXt}u$[=&z^s hۭejv~6QPO풷J#y= 9 -㦝VwXꮵ2toT:љ#sq.,pC,(6gxmE߶uFg!Ё `:2ybF?7Nm 1 )O_'B> \,<3cT4VU.gt3q]y+G&Pl sLěOu7>,U8t4̿i )1 ׋)jmxeWFNCJ:i#Žvxqo%B"}*WkytM[+Piu=2w;.A_ =RqB!Qh OxFsV,\l@-7fD/3S8lCH!FB$Fz7 YPy%k f ˲'=⮸f1Y 7pFh'j_(TѦmN<ˣ /uΝ9<7P=K*4{6 n/FL-ڨ;٨*3FH KeVNZArE|Ȟ"!qk S|7ƪ64$7 Z0+&7;dgT-Z2orL]m|b"pUf@aXuzchֿwZw/'< L>"*Ppm#wax<{ӟ3*EaZ8T)հ et5##Vb=`AlUN(5ԅi8rtVb Ѝ&qo6o@p.J0eRÛ&|j%.ȕڈV38j k={qG3Vzik\w0v_.vt rx 4|λ(_?+}#Qp`.䒏Czj-kwu% 0)hdvbՅ(\~=@}`Ѝ&b$T9O&|[`mFG.iN蘔':ljLJgc߶<̂Lb"lk z(ȄVʦרV>g`pAiBjbQyRUeEP0y“9p}"WGpan )#栮iaR hR͖3cY]ߔa;anv^͐q-{5~U eőeorG@w ǵF+<..#lҰuL"T~Ϻj + ~kS'wװ-,)j-J#4"O2PJr?D|goRX8DSZ?5p lEh %p"rlII'ep<2 :p0#20di{$.bΊ.-VGCҟ*XJ'rq=do$ޢӳcxrP _"(n- [X^IRzgsL_Dn \ ;?T_^pI4 |D3;DXBL170B%N Н_bzϔ4&2KdD<&lCc]uՉjH(a?$35C:P2xɨwhdβ*&Zzcᵙ?U+68/h S3}Y C u9d\؞J%h}b@NJ$!ecZ޶' ˕'\I _2L}UtEBc?w)y*r0\Qq]10MΞ1lM!u[fҖ7gl`ǏAAT+y3mʐTl׭e]"'Ćȵtݕp@c PhO[yJVot*Bl|$EQ^;jX3Qi:L}3d NҨ!RڿJ;lB0ZSaFd!Qfk))8`T7ZyTr,?"NcQb N V2ZK > VD#hhܔsZw<5pG=Oq 譔5O ϯAHnWusɰS:ͱ&PD++ڋ CtgeLyzkrܾǔ>= o`=R@P;#tH޹5w*sDT)<@LdֿضK73rsyj(-+8{aK?_OyŚwObn(|1K8"2IPhx_^&g–MEgm%c&GN݃zi=c siWYaw.v#-Z9@랳Q{ʷ0|>\p]WlYRHu5L _m0BQBѻU\XRL-nIrdh0?߰~nAH+8VspLh7G1%҉HPm'MiMvgy9gA@A)Ph* q}:4&fBK]GXg$c|xLdPHV8t}!ǩ4 b@fRZkZ1:ǶGۤ>Ё KPFLKUݑjUsDH5*"C)i+tQu)y_oV;Դ8hoK!"gbW}-! |`^H;i0c-6Rh8w Ȭs% 3]?t)R楶 y-AUEBkoOσ]jօo߂8]{tú2e1> 0ȯje[ 7.zf^sǛEEМЇL1TXB?U$<#_"-OS *b]L;y2Kב 2nGߌdUҝE9F]{w:x W(%,8kPv10sIگ=|/a>˵=_\9޴hz4MJn?;a6B?wD*&3ClJ X ZŒ2L,Tt.x r#mlU}I7!G~9;~!V`ܨPE9WJgEL1)ۆ0,Ȧ &pyhܜ_$>go0(0U,Yhg}a!lV`{\lT{e,:"H̄) 3Cw㕩G{0&wBȽc{ ,'j׵J^ϛ1J 2F)i)XpQ`1LpPNnVi¡\=uN&;, 5*_ tOƧ  Nc@%&~"VA4uwh? O~vbpkf-ൎO#ջމ%*9F|u][57\#,K@on d!~~qYNaԞ5,90T뀑.V&)(~\οnF!OyȬ>P,@4VH |lTd RҰ-&{r`u!*jrF_5_ QwY ϭ" BhRRnLy44yv?RoIGj+K'/, eix7f7 }#6?I:}{͈d2l}2gH"pv) !Y7cB`")EQ$#6%8v(c2iȭۯWQc'F 7:ڡPW (F,`%]Z*`Įk0eev5Ӈ^?@hLQ^''PIMW{/a_2>ǭ UsznYM2z+!nSliiTqvHL,~$d# Y F s'MrfjFx9LuI~e >@Hz _3 Pt놺%Yr}JS|_C `~Md'\jq,B2K—+žiI}ьxu^,*ՆCBȜ4jufS -y백'],HD%Í`Q֔^ ]YѨ[w7R+ #/+C23Fdʯ4ټ\vI-"[6bu\_U hL:!ѤпZ~FꕦU<{%6 z1 jy|Hzx\A {{tձ0Op>Ma}>AacM<2yrs$qMe-f8~@0 *綥oվ)0#*I] Q S{U6^ yf1x,YXef}l3|:4#>vu8%N F#f.sar ;hۃנ7NZ_& G:܍I{/(8mVs^5cgH~Uxc[Jljrt];R** ZS/S+uK$,8V0v %Ni N@2p ܓН$\ }Sِ -S+$ 41oV+^J3{^fj8>[6B.H,9݄`VgTӭP #;l3wp%>A'qNa7J>V ۜ;>IkGLQ=h>^:"TXR1o1F+RH~)05qoAl[O5-fmk+5U Iz=kMc6]_"JLsZuSmb] A5B5w5!Eo#b#ym{o1Ql%igZ/ax53"~DFnY͘K!AlA*ѯy *zj<K׫ zUi8>No:-s"6g :ow·Qpi2| D G9d3`"NLr:YŠ%uwsB` 9( nC; ,'T fHeٮ,h,Z^T kɸ a^]J:|;F]kĹ\ůkNS_)"c.u h0sڬ9'?O,>Ʒrc?8$1F19p?A5+LPQ(bAhAVhv|ɑ,Exz9H!;$ts i) 8Fem@C[VY4xtEp]w(;Yf]@Y3Ֆ'$´P$MƁ"u[~=(x2+ /Ȃ p?eJvw%Y'arpbNpi^xY0CgN3 !fZ}&`T3_, ޕ`*$RS%"ZR^plħݒ s)CivZ6o;gX Β.JU;8h>q# !O]9A*HkH+rۚ$M\e>+G䵚Rm6o4>#zj6pY F1L=cp9zrK!B_*mpN^0JA(aV|hյ_W$g*hkWfo-['1]YPwR<u:;]zY}q#(p,4}ڐ ($qv,* Em#yA9M  LA)i^2BK˽F};ҌE`O)HD@䧞54@jRȸ4W{$"3凾D8(Oowzvrm0ZPu;xeLѫ㯉bi ƪ/[~q3MfMMA,tHZYv!Xi,,/PaUuF6uBK]![rѠuiބ:qL'1!RA KClayD 7}C¾>.6:DtMl 蓈 M `:sa@;Z?:\'L mN%wC$m;O]3u[[tuT(; `Uоӧ'Y͉DEn|{EF5#U:̚ n^+r~[E}kkZ)*aDɗѕqNмDKq?-I3 l^6EVfSRx3 ^2)g:?e Es&_'2VЦna=Tr9 x6%]QJB# +[BP\ JQ|z%ŎV Ւ"c{-Psufp8 jRӟ"X!P8_c;in8BYi-v{pGgVؔEd"o;^r&չ D׊ D(I[xxH˥S*()X.ɚelxd"?F.7fommiL:^ Ip%ISOA[AG̽RYKu˽?'U bLZ?6i m BϞŨQئ~dȖ&H8$u IU7\^?}Dn޷,-n7mK Dұ0.9@8cIU(JPsCbkd*T/Y 'G0+l!7PuGP^%--w¼5 obye {"`ʫaYsSGS8>;;dR DOQ!cO$R]GX/emИ@6gLUF1DIa`?Cz0ófhtBbОp9/_5 4[c˻ۺ Ux~K@ux)!F95ΧмcuɫѲ'bףÚN PgtbRp.Yy7/Nx$:็r8uS{ݯ+ldLjשav8+@ɥ'SKN|!ۤc~l oE3Wi)W0: ?~?nuU4J Q`9bԒ]Yp-9aFi~_od?tٗ6,%>cp+ V1[<m@LSmSTb\?}fB˅cGʭϷQSkʒX!=.*h&iwӷ"9LD{LKTq[;'cLzbIw:"g,o4fCT k7ۯݗi Yq".}8 -q$Zu:30hRZ gcBpUFyd,QdsK(%퉏x$FUt$a20 ;@&۟q0K@ѤzHUL 'E2㯙x4!| !Jv1v2 X>;"| *XƼƽPk?|>JO!N7ܟRvkՈRߕ O6D_%ɸC&iDGˈ3ݤx)ñaR/̢52H@{vcRdݫc`^a-#EqfJ]"D۵E\h32Iw#Q:P\: '?kZ=理0ZV>#ǘ{w+xsIzkU>M!%SCamss-م"96~\cJ=͌SRhAF$KYS ֖)Y2 b_\ݨpq%j ~QUN cE݆}Lg@bfr@w+rm#01O2P}~[u%Q-Dh+LuVKpF7pDM|_@L!1@W4yH~]'>a3»qGN@3BB.yP #4bB 3x=Ń J+6.2a3Zd*} Bylq ;i0G[o\L5Z5 `\7a쀸 ,-O5K1xP5IaYpx[SϦYMCu3([N]#Qjp#9{]GB<쓳"~Pɯb4Dg1`-LFA`z#XPB9(ڛwۨqbC-gT8_]USCr2enSfjz c F*7nЄCrLUD/ji-THy)_廟ՠ:,NA}\?q߬>Xhz\ּz ٘ 3cana*,H_P66l@ / ͢8IY(fB %v Kd]~ 40yA7&N{ "('7jչSEnpqz06u^:l݃8 ni77 mg4JMH%꺫t J\#E u4zUٓ_yC\Qa.Y9N&8x*PVҡMvYy^fBU_k-bYȮOa3^w%\1XhϷCO~#H@!qe)\3馵,]fFNn*A9/\"%iwq+PrvtQV/B"_n`KAi+e`ru WqK}JH`珞7**ya݇d.Oɠ:k#p_)^鯣gn(:BYl2oC:^8n޴HIGEV/Nǂj}ZNX,(!X*XͶ^BTl.>ren9q*]Vz;Hn ơ4=( `Dis1џ(E~ȦOoSԝfqHwapb23)E,g";GSxfׁ;L#9lnդ#^ђɹѧpo}x~?}hP 7*U'G78sN%H褋"l]l AOڭB9㨑kkx4(*߇蔶&͒E4ETJda W)?lO9{A C1 㐢ҩ;^o(){QɥXcw<ߓD_߇bxb0[ e\f MOAuAU:SU^~\8 2s]m ЕtHOn)pA0㨘:Br2w}_ܔj2}ׅEl0{DwujI!X2wPw iuE"SSJ4x/;KI^5-.b3_PuKdW*" j{uK@ծWMTMSV[{o~՘nM@-[0@.!uߢ s\c;Zhؽ} m?سvJ@G \ONlc/RL.JH ~NAk;eLPu\͍4ߤVn,s]!E y%ji\ `GEXM #Ļi>^6ڷ72BNu4!o6_ZO=6jrT}B X PQU$:֠ V;z*Wo$@mAQ.]tq4%=VBF"UOm7PH`ۓ}EJTk7 0REr/iyRh uQqo5>J=ZZ>΋MHHPjkV~pXH딃B/s+*C_ǹ"~lAO؀+kj,#yNd/ Ϣczz(D֫i8 8 Sc:zm*P֦g(ϐ.xƦ vX+(q"tEsQ(20YWQmHYD2 "H|x39I%WLq΄*1Lt<$ pW? o\Tqp|!!Mgmy.s 3;0IEnƏB}0{6a|8O' Wi4Pb=vuhuCM28vhxUf@9UG?Fy>)ɋȄW LxJАMY%͢fW wEg t@X*ö!'KVvhΈ+OA -A` co;%~W?C<,h MRҀy_-{߹'1Dr\ְJxzF<Y+`:6m#d5Դj-oR!?f9qB(BZae!ȝh(Nzf_}XS邈.JjjS"Bi/w9y e ΂CdhfU)(F1 fN:3dІ;廋ܘ鹺x7 v-ї4auB$ qovvl@ّ#EOI)\_O$i:cKZ/(aqTּb fHQ ulSP3MLc/6N&hJZV̙oiSYQd7p[.QVT-X `Ώ t)|YID窋=4EhCCJ)DW%'KTAL!3Ē,HJY/0 ֩CeEj>(=)}Fa~>+AބtƔ,M"NW|6g?i)˭׃kxd@պ`m𽻃 , ^彷kMĞ^gOJ Зgdg+yf>d|'y=V; z BaG˓wAD߂X_`1G][ZꐜVD'=z/LI ٶ!^RP6ш=NAU_I3g]]#x#՝/R{:;1XQT;u%C3n4`\g7ۯ49P[Fac?BPݽm=ȌXF$t7&J;\`U$lxYů8|NHf2jc2/)Lj⼜skʑeyzB]h|pcㄝ@YĂ}CX{ ZYʬ&FiFv@8iڠ:^&x !G KbbeELa\C&yHO.tQanyaՅ}^DB++}9rK>d EcsH)-uZ;6PgvF͚M|й(KX ׯ}g=* ̇MV8* UoyGf(񅘐 .kF%OXeLટ#(c 2߿s~3)o{\P\JPϯd+Őb\EK kshiU̇k&߭Z3y6Oh;unvaXcu +r]L0!J7e$8gOwW(3~)1r lbތ?2g)a`I֡iۧ$imgg X~J5Ě 嘯7{iR/@/Cw:ũJyOwoz>X yʪ]h] NHgo=uQ-2QIi HAZ3ַ(#UGp\J)yT28vC C EŃ3Nb/]S?Mjx҉>%fx#}8wGqx~b(zboa" ڈ>@0~"F̞5ȼ6h٥ =IpcawPI"oag's=ֹS1LY0zl MS B4>|2pVl"B}2U9A^"R__ҪU6Dq.|NҘ?UshBt&-cd oq{D[GOo@Փf]-Em:N O#q`iǜG>#Iqۚi8f^J <cSD#,~jrF9 w bim(Sa2Y V~l? R/fN>kMw** Ul ?~zCO'PT>a3!h#ƸkQI0T>C!ϜsDN:'l1]knUiX*~IRLn&˯[fl 5f`A ~d %) TSRcgU΋1CVotQd]8d!<#f] Y6P8o[PF( pǒL0ٽUԝioe Fӭvju2?caRTeС4Tp@GXYtLE{Q}a5xqJ27 k$=]B883*8-O^c28C.}$%4j ;Xxxe&4. N㬈u,rS6f }1F;,`9Ćo9 ڃ[ r$i wTCFyhЯy0[*P ?lmÿl}Dq?IO }Ck9m&cFqES>2}vhM)Oć`v83ئ%&wVY鵤gTL_bFvq %K\*,/-@XF: vѪuB]y4cr,H1S90q3|D)ۀ^Hd__sdG&Ufl[^ˋ$!N 5DŽrrbBhcJԳϤdN=m(x􋋝~Cp_e٧0T2#ٯ45yD螛PNJ4/b0J%ւ&3&r_0UcoOJWs`A܌U\_<>$Rm1kidmSm$H$H3y{SI;j8m_h r@Yb%"KlFrI~~+wn:u.|ѬjDЎg+wW%t=0Q\%Wr|XlVS]dME1[eaUv&7bTfYa/ET3QڻӏP D|;,€;=z~yƀp vf;RAlqMa|wOyPmZD5Τ_m\ѹLjAfXd6no5Z51N֐}&:ɏ#JxϜG榗|9fc7dԏ*}!s:%usp`kXEByOXp?Q٠kteZ[c/JѻcWM Ր0Y~`;wwTψlXBXU/Ceĝ :\vIUo&kݏ7x+Y>Iu"T̓1  x:cB}\+4ַ8..]c6FNbbPGlB%&F\okfyqIv&0}E:wՠGOۑ7 Tza8Lkp;)ur7%2ZH'dڑ71ҙ6֯cC, &rIPlqBkNN4=e;Tͩ;jcJGQ *tGS9'T2E;׹Xٕ 6m $`!x/jzc.b2+FHhaJíV`VSW,v/95.!nnz ^'YY(Lubg>& ިKI٣1 b@K] Y#J8gC~bƳW6PgB]7?4? @@<"5ɶ{3l< -'+)Pk╌D"Q=pu`V3N@-ʥpЄ/-5O[ jo}uOy2J,^yԶuWM+-p/$,ccg . X,oWWRll~ ٮ.̅zw\4=)ZF5hbA{kpȮ"HȞ{'ўL^zN !D5MTK]0}1k6i"$|N1< B jvfZkOh`yФMP3r*6d%r<Ë<DcNcÁ? sRL#OZ5'4BF yAǑ}H%Db"(P@Aordr]JH0y&0;$[^^݆h#@@V~U{.[ǽֳGPOQfMAOdxJq'"8mhK:P <3 }P*` B+0%&:̤j6O>~jN~&)更%*ogm7}$Zx tOGqY 6`e2ZA%GRmfDm?:qUr+r9U_C@T1Dǡݱ;[:^Yj~T'|f`*Dx_)Y/`ށ+7[I-4ad1ńRls.aMgi* |\,Sd/5ܬdlϐ"pgv!SH=G!|*-TֱhHRplvBC:\>cަ\;61jMpMkS¬X~ȑ(X0c3#c4,%M41~y;{ד:Á@7ONl4{+G_!ɀӐ"RG/fuh[h Zlkc$U\IE{0"0w[ϡퟵ_zr"n@n*[\Ww ^%ѭ `; *F3q.c#q&wj/%ߋZ`Mw`P߳1Q EyFrj|xʃʦxSPy"~b8zMof:d+ֻnba7Z Œdj<\/!,Li8&ۼha& 7yMzo_R~&Xĭ" e\^F) l߻wF:m\rjX6|X}oY:G?ԏUBP("-]]u$ƉS XfAbŎ`͏g\]aT #,>Tuʵ\[| kXf^B˹; }{!c0Uk}d7旰 Ru lTLmZz2, +6 d0R*֩1-|Ψڏ6 Bfqe7Hp$C\Ȏ+R?C܅y[1t."ag="\TË́?I5g GVz Yw' -<Es\#oGY _,ZOWn0 ̚>]i HY/HtYQns=_p\T Tj kV Tc)i c]ͬ$Z43$aVئZqV=V`M q6pY?C 4%S{͂!g#:Wb6ʍ|]zt$L~TUdBGU޻RCw82J185̹a@'\vmހt#@߂by0*Q.(Xi]=Ȃdde 1adcnVL ْXt)P̨Ylq)5$1~~Rd!䋒(Eo,C4!D#t OؔF$ȧzz(EhAT4>`i!VՖ՗[_^{܀!r^tNG=&4 ˒̄ADHaA_,CzG`X !(u1>5F`Sw5JGyϋ6æ8ԋb lGʨ.(&$`'PdM:f5?|ʐbͿ<^έwTE2`mk4 G8‚5){WT#BU"/@w7&\xݟ9VY;$DԊvRt;Jsfތ裮."rH]_^_8]Q:3ʌ<_sd0/%jd״9> A lR+k ẏ[BOaX渹gb3afػ HfF!DgCkO 1?z}h*JQU6޸=ocD&h2$ߍDҖbtnZYDPf}MH'')1 ҡk_{ڰbNk4Vig@3xwz=/؅t#zLݔZfoeky3ds|rӒ^[Ϣ'ROpF+;^@h<%2*FUy:fɐǔA0GmsDqa|f\.=G##j==` t9Om%?E,O$ʁǝ._PHSӵ/u;ԑi(m`^om6}躔]O#;v+YQw=([ X I|M! kH 8n|gT{'};t5>Pf\$]tnɼe)b85z<*2kۗĶwG Q1, kY B,J>LpQ+jp0B}ck6/z6 JeT#=0!0Z&_Y" .tLJ]c"O(^S%|ç QŘ2x0_m IsDpc-T L)Y3&A~ úWJ- +]磀 .@n((n+U:GckV_jtD͵|"Fj'-奂k-l` ;_ \[vg8l˲QJru4EL|\.|*n< B"JPLPMjtwsX#:Eϔ# 9?CfXQErwj&ğ OWFV͂^8 lN18+W+b*M٤'{N^9gQZe9[?"b`Ensd;;ps:uʴ XO:ȹ< e3(XT39n[셔dEڊw;d_*.0aؘS@}!\RA=I>񁞊/\Vq@b 0c2^r 0p9])G,sAF)?B9\#( Mt@rhI(@D(΂q~Po2-[WVgA0Dj,Ts&]{t2j"Q8o3nlK_:Ddc_v UHq<{zi|ѽQ[EA@p I}C^ KruOg` J iJO GvY,S[ne?Z jC vk˼Nٿ ?՝~5}ݚ(:s9%}Cʷ.;lQets;m֠<<ΞMN8T3=t.}{Rȶvi'x(zYLi}"7 ׇ{ktNwߖ&c@e1!XsXwsH4')THSOSَDjH$i\ 㜭֦qtA6 o|96%ǰ\TM*kCMQ6Y:s< ס]^>A(*$9e[p(Fؑ&UiFۇ໅W%r~lȢ&m-7PӺդ3>TX2#Ul|($@G7whPphTPKNOVH@o(A"*>L|i3fQ_r9R3wZLR#׊ j 1J\'Ym͗T] V?c8*u\TS.=2Jlbali+p!`@z]AB@TQ `GS.X^N(Z:8z;Ӻa |v.Te;~.]VFcI" M5 E]F"rt'?eWMkLoL>N}D$*`!3*@M@b}4Rl`KI5 /q, -^t[8,PvxFmbj\2%Ch F\APa/Lk}yV TSњ)u+(A";q,=MRHNPBX'_= 5ʉg[Cls ֛85W ؒ$ ?۹\>p QL $I~C@^*izRzkП15̐׳Iz҈ 79&j2W[%x+@2xMA҂B_q7|eBh0Ag6 |A.S눢-x<"x_w\x1rQ>#q4|yr4* J-b_ \ kW z!Dx& oEuq "q&3Nd3C=(&.Q=DN8IN{ ӡssYdPnlz bOS+H/go|^7 LOQJ{9—RǀCԶc>4rd޼,XD4B.}f +OO~1E gkȑd_L)\7@#~3ʔw"+8]=?[GHvji{UpWerV5{P<-\SAo\CQ2=K7_2 1RSܹ:,. "nI)jBϼ?FG-/ɔWb6߫JQ$p]0R΃9^ܔGNAD0hWcCkWAنľˊvXx"&"P1vP{%+~}hN7#lIo)Qrax/؂~WX?j[-eHvrt?o0Fg$r;R rI7F2M~a[Bz\EQW>v b' i޻HOb._ 4egƯ> ehe0Y7krH]n6T7.4 ; =GKF'`QpB4Iy*Aj~Oyd +޳z?3I[odqj Ԟʠߵ(ogpQnEt~&z69'nbJqEo4zgQGY*jalťc_\Op0O@(POsHI4.d #RSˡ^m=aw=0l"Jf'=4?ƽxRJ*>Eoy>;5 3وx;&߀E5yf44Y3Fs'4-GoMQJ;_B]> a:#=((Cp<ܯ$|μo2=ֿ忎<&5mWr%j6=@? ߇fm܂"eĝEp `.8#r0ʻ:So@qCރr&F((d M-PU70d  Pȑ"1֏ (հ%>9^$򃬷;>{vxCUul̟ߊiw76 Y$VF=v -J8R+hI333Scm(qZENL4ٍY9Sb@Ɇ:fvMeŠC 9iG=NIRh6l򵇞|v|*N>JybXpg8W爥|noBĊa >m".Ѭ7^km;LOsvN2vJ${CNx{ek[[ 2VSg,b|%(@t )C&geآ3vصOa1@ nܐֶ9L2hn\'J[Eb#PR!4pF4s:b#Q|^Kp5T |Z^SS$%ܪ>W@VQ0,Gqx ϏF0C`͉rf2J2U"&A $"0ۊ4#4Q6*%ǫMVz2VC=ZX%sƽzZ|8KH4q0wc%a-U&ԧ;=}dGfJ-#&핽# T` S0Jxa6Otm ~2%z(2-e?AQRSob%BZrNvmWM #~j Oo`WC1'/~_'RFhR;IQSТaN8[k{l)|fx׻pzbE ߍnh<(OhZ.hWM[ Lkn Q ^N.إ0I EE!rrn9ONETxm TW8F,[2͙Q,|6R'W>'tUFmZw,o|·0V16 vT}=wE YgGv4ԾxP`8ĕ8T@c9x ) SL]ܸٟgۀRXlsfxD΀.+#ݓ,f9~Ffv9=Y{QKSb㺕H6`JHq5A ;)p*Vܖr37yց?=1^$9>aʶ+JXϊ pN  1//a\ [vs&x|ɻo=ɝfK/LXnw9x~.?%R |4,=ߚ{5j#N[j%t=DBݛ\"E_d>YQ;z3пSE)WRkFf/u>$54n2$ ߾@H% yƬ9R~*,`PL+\{EH4w S{ls [68bG$x*L;/aF>+/H ȅn<3Ɏ O8r trكa;J6}L࣊بISBWd W\v+]൰C%,Ċ:qP4Zs"gz Ő&@L8yAM -} Ӆd:%G Zs+i_+*$y҈0zv@tWW6{:a>;4@LK`PRu)Zx$4BqB"bVEI .iY? $,>$>M@5H c,*Msgd95s:+~~>\:ҦA\a~MSx6̀Xrwݥ3C챳VtBGq -QV8"ɶԂxּ[ 6scE- M֌V% I/>ѳ*>:i1z}Q7UJv}NȘa7B50VXJɎ8jM 6+Vsjjc 2 j$B%2sI[C mX- ۶igTs|笆l˗܌DNj&'BK/g7fO. >L~<OQh":I%EJ,6o^i-_:t@[AyRhߛ ߨQ"tp?f ~yl(raR~_h鳓Q gׅXvob Fϔ _ΥFBN]_ }]/̜k٣뜔uqo{CB@xVyA޸f>qa^Yq嗏niݶTm(Q&x`O__=WZ\E%i]$ABuR~ st=@6ju )L{~TP_0`RM8n9c o23^\&,-s;X,I9:%>B`>au !.Ǭ~`T<Ghik4(]=VYB=cMSJHqGd%",*?BϹp|*J)5KQzr֡k]-zpv_Z 'ޱ%&}1̗<:t;ueRj]X%*$rQB;\BC|典$qD_3N@o(ZC[h;{}t`JH0\( B}M󌃻}ebSʈjR\}, xS.>+RͭdA6OAV6SW0펙5}Bw=҂xX} ýyQ[X*-Wţ<*GE]~9aSi8pVXwkfY`ԋXu6ԭ_RI_D3$5O~X^8}hpQgWc;-=A`:thrjQ0b^"T)a&g 5ުD=ӋP92#dPle\q80P_<-/7]MwUp;8 j*J&LB2f6hۉz]԰'$[Er(?/jNcdl5}x*r*;V fLj6gr.xVB,_pxf"mOw.fs[%.$ֺQci!k&qMS@F|_0/*n(9:z5{yڧ^Tp;ovC/)sCBc*b"@G\w%9yX!p1߱+^b~ҊYz^.'].% j$rΔ]AR U&aql#qWP7f\?{M ,7X`< \Pr`;3b+ g^|a/"@@SK#X$UU b>z"]w`:2N_sכ Xu7Vw@4 VHKS/P}};Hi{ FHC]{gHl)[݄L֧BhBnϒ|H0As1pH(޿9f e*c]mٗcA wgnuɇi>A} ˭P_!İԐmX>~4c' (b I5QyۘBvvA> T&;ke1{>iAJTn(fnlPׇ 􁍭^jtyظ[KUl`n۞T7IJuV\x&P7Uk37@D{Tx wrLV\2mYw䒆&XtҁvwZN\ߗZYg_Kq_CT+>a&ZEt29D$ { 5h')Y| c$Hq?oZӕUi2HU`k|GDR=il?& )>#$T((5$~/m)):n@듛>`;s ~E*zдܣ6worO6e|d[[aNHWjD(.kZ-8uǿiu 3ޮw赨b6.Z`K졕f;s{{(Y;q%fFɬ.[*t'eҽ b▿06uٮn9lZ_ vMg NWJKW6}̆ͳ0$,U9;"#0*U#6f}]f Tl;Pp< A 7%wHl +blQa<fَDi-5򜭱*0`ԉhVǬr0G:5-,M cpp֚K7P|%d) ˜|aϗF*7wG@Z#aAX}0XdEnVO59o? N5ݟ:,?~FrʙJs!'$e9YՅߐU@oh ˏx ]o5 'yLh0(UJ<syT84#L^ӔPDnN Jz6-.r\~⍏{ϏTzjh ja4fHe8_ p6= %=b4c# w+gw U(hO V+X*>??;g{{!j\J G{Ƿw<0VTS: kЂblrJm[Z>2Um}'xʊ=&̡-JjCo`B"Ywbh__{O 0mW cn Xϓ">ؼ5>Ŕ%Tc8ql(Qi?Qd^`d5Bp+aBy\4ίD?Ô0o 6h` hAut,*OYiH>9hZe1ltlRtY>IO(mVKA VDQ tpg 嗖-<FH~4c+ D.ywc_Z Z;9bōdˤHr4~عCAnjaRՖ:S~aI'7Ŗ[>iSD4d :ih`>%,Ҧ5v9\6ΛN*L(.L"atrhq _wDkXǫYEx| ZkUA5TC‰)uʧɒ~zA#{r? YŸ-$k'LwO[+gE5mGUzYV[(ltHxH屍+HQIW9pNB)}\͋J s ʗYPڼ}GÂn*rbcDow9ۓf Gvį*>0dږyrv1*VVle| iF8π/c*WLs fĄIв2q;R1JG4/\9 @L$@m* {?d&Q\舠 ! JoBI&̙SRR Pb5qGo$X|=yc|{8j,I]AzFI wjPlɏz9Y'|,d<|%[ S H$ [>pE~S2c81HObR{ p 0B}%~h)8YmEH⴩10|Rl?e\eh3 otNܘ*W%)n01@o XPAЮ820y!UPkD=Sc"'imT,\ _Z[Qdl$yBrH  7L^]=kfYF᧰=A9rR@~U"Sؒ+o{gs 86xTKqT\WxRg^\/ fjchP2E*+i;v3՗*Qs:̛4g| G?}E[%}KSd}fv2xX"b♔HOA ۭI`ڦ§Q'T[A'-%K' nuP[= !z2BD)l2m_ ;6$Q'ľ`fAʻ4˜>DzȘ"}kI;,>F/$Qߺ:fwN5eSi|κ5R*ipAX'>h-' 9pk5d3A*&j]j0n0 ZREW&%K4@ |_ig$eeWP!N?$""2t7rp@ʺ"nmiv|*{E@L<||r{`c4:Ezkb1_@>Yi]?#]"F؟/n- zRMpVkt)g?w>֋X$jB;3yX@hm=-'_IABPJa%N;"g!N?>tlMwjs5K?vw9œlh.WR= ٠YB}#*7 'EUPTPտ 2-m"=_FXAjgeKw'K#˄j)8D熴sYlKz."c nƵcao勋s&0kB!#н[K XBsѯHF㚇xͧyjU|bKpBa^Q/I?jJ*Qm],dE J NF=̱n8n.e:3-OgӾs{NȴtApJ&| - Պ%R􋌗HTʣY\r3905 VuDC CMLybɝu+mqD/̼|AO`my#0xIMx8cx̗*kqA/liŰWrizWXK5;Z!Ydۆ/+^tB0Ӧxee97͐ tBouDQ\%RU"%G]f.'k5y*X'sO7g4n_)z]?6C8ACV]d֚\C UXrscݟɢRX mW#RVԤ\nH*^JŇ ёch`|W H"T\P! `;]O.ǵ琏 o:Mmܰg"sًTG[8qiLqB E>1`0T7XYXڭ8ZEBٵ_Eon7ٵ_"! GqmrGϻ-Rdut'n:5xkg0Bt$z c,OFY6Z+oi ;Ϝ4GME~$N5zw2*ʏ)ԯPCFQ]^_2<1vĆG*癤&>HXҩamZp(51Rʶ_Yw$8 kMw_O" 8K<*/f{ΘaYPAi(nhݔ,a6`rKl6y/5LK4͘Ì!W]쬦E[Br@ۅk}D=PkpnJcBSINUR " @^+gA/w)zpݏBʴ]Qq~#?n [УK4%!Nc\CNh5a"+<?K$EXm-rUw`厜"MotO@r<ioDaFSֺҬM{f 'cgˍ ](7", ii E&[Y)Y¦@})z[txFTǏq)e# P" o@хJ Г{>)^ X0a8 K<*44qO:2u6r)]=g5KZug>d)/'Ebytφ5鈈C ۩r~ !euII/n MHv9>^g;EBٲ$uH#|Xel,>˼W I76mIRB#i q-]sgHxu*(JMmt%j0RaEE%6Dm A;7%e.ԊYso+ Pͣ!X9) N4硎`GQ]r`ϵĮMw|sJl>;A4~2N<.K5ڊO-=@O|2=fYqɬ ^JV d1(Zqsi,Z4SXJo*H?<$YhE%aqZ,?vNaHZOpqɕ+iBn[Z//%c.qR ~Z TXvނ{m'kH>7LPC9vf?W鋷( -5;&Zs:`+ VwA Ȇ$MVS3N !zO9t=[RwNQFeii9*:lg|9:0#oē%:,zh3!S=2'fxXkOY(1AKsNh=ҍQ's7k{ƈjZARC"/ 8v.}6o89=ejF$ENPjgvak k{"%NCߛPoJ8}:%`lrf@[4{US/ɶG=gǵ9M5dSd=C;48A2kbk? eo5c AjiLLSJ&4W L͟$I@1MIB*wWH|09tPu5|PrB@$Ip7T[;%f*jEIV 0KFY *LIAwV7e3ڬ-gv w҂ Ba ]mj+uEDŠ8ƾOvvT#XG$u烸~y͞"J ntd,[XA/!=xypUM,*`")5 bOVC5iTC45(wv&QCZ 3tj0H44zp4P7@|,w,pW&q2C~Ǧ2wkkQ3;VZBu1a Sz•<:fp|3!ٍuʰL.XB3 ܰ4%{},7{cU sOe#U3LjܘɞXMW޵+t*@ܢ{*t'^EƟq|;űcP6 'Ǟl'V >)o j+fDnhZ@r]z9EZ0ʶ7_u]$VD \jAޘ O.B@B!#j4q,e-}Q5[/aq#)jo9oD4 Bd/>R9޳+#|f`ȹ M)Z"v:{)6s-/.x{3 QdDd8Q5[$0"%<$X ˱#cAnWɋ,r`kC;*Jwuc+'6?>$;hq?$1 8̔9z%3?%ɤ xAl,hLz >LeɅ1caZDjAz'ni#I&A%x;@ ^myj(J.M@HWV3Pvz7v+ +})b6Z(FaO&:*9ϭ` "3sʡPRFV_֍ZUFxN`湍XdLsOz-O%^nlIct0?vT#fQ?+Ä]`{qeQCevנ+>UWnz LwEtL6L\"p.TX) /E ܡ$Fw8vxXjVTIx uoVcmНֵdoDj. / F7~x{DA }$ ~ VET6jNC?W{>ԕ%YMųHBըJBh޼)KE:ckh1Gkfؚ,n ]c{LAig*x1;,mbD܈ { LG3/024VTTP}o\)˹6 ֒6`PYt;* r /im<%p~{a1)O]AP΃ O1Ke^pX\\ڿT0Vmlϻ{ nO*6YJjWKxCNyx2Z]cuRdi,eo3!BLZaQp=qFc| kɐ8b~DZ88DcE7 V@2+OQQMH.S`s,nbN)7VilFH/C..LŎNj cVk8!|D$&?E?=!:b*:s}qS@ݪ/Kϳea`%:`#-yl3cRݠ$h@܍d%-e eR?Ce $h|ZjItEʀ ~3#j-]23:]+>P<1fbj5}vpwÎH"]\Sk/Ce7CV1I-ڭLYp`0"wX2ޑ {1ZWx,}u5#,Ha4j{ ?)?[$G&~Lq`&fhϰXc DYv},#hٹZ_ӂ#gwG#BOD :O;&nEcKE(3Z=Y9=zQXaFy;|6"w=.s8?Or:qyr_UsO"n(HW1jm+ <$N ^lb5C+x 8%HZGV\$nYbu7 eAX4Di 6Gig!~D:FI " {[+ t-"3f!KN^:|P;nO66SvȅM:մ`Աcw=bM.d ({ߐ& S.2]^[@0)tI'LBd! UY056 c7cҶ dMR?EAv ;tZpz\֠S[7C,h=q]mXR~O"8iZԸhtl;2X<ўAI3&NrOoI3R@}dM#$I.bc_"[UO#Bi@MoR<%O=u?ܖ6 {{˼ fa$}E'A~"l^Y;Jgν{[CmoECsSֳHطr!]y&/{Ԥ #f@햅=cU]Hjx˙.h9UCS vZS cp*}"[Qb"כӰX9-W)A<:w`Q[GYI7(ڒ\mزc<ُ|Bgj1n+Ny']|F Nh`|qytSt8ӉAAoq{5f´NJSC-M;R_9csgA$`C_2xJƅ}gC[6ʝ"Z޴u$6}@?ϸJԽ W|+lQS\ז2y<0[Xߌ.a& =uqaW\GPlyNW+ԝbx:59C}ւFްskp#T>u6F=mgV+"Cc'%4PW]"hp!G5@otg@> i7b]s&O"1`]Eyi 㽦s]nMw$ =~G ;!bI3"*er@ah  &ˌ;3 o*KGM}Z}>?ef$d0O=.O<$:O~npQD(?h2 xkCi$j{A_Ǭ%`RQStן$!9U5Kx53J־˲UGK85Aw(5p f=Nex:7;]dd3# 6ݒS1Q ɌAgBVw̩HK郊 o| =8_ Kouҷ^@j Iz#kZ }4ߍ]Aވ GV>i%բ(-}Mv:Rlʔ`实=%`:Glv9lx܁] 8G k%Lro\{kH+"KV\i$a$C~WbOE fBXNhibr^թk>R1_#uPMclqLbKM?U龶d^Y 8#-kP}ef(c(h!#v5 ԵPKޑ59dz vr8o/ę "]a#~)$zD|xvW< `vj"DޛK2)@ɗ⡿|xv(IZLLZ|7._bO|R%sZ~eiWs ]'|8G;}"d5(pN翜e o^#veZYDngn{R*)l^K #,Jy㭄-F`D` =hoϞGF.2@Sxhx9 V998 p']zDb:ܦ^[{FF vA]Sq6O2Axe:GS(fk[ԟ1UIҕqG0t{OBK6d,GV=ԽaPӘVv',RrB..Gn$sSrw0Fn%rۼt#ɩLW~w/\1FWM'Y.ITނVj> Fa68:U?c? j8ϺX*f JV0%\Dʦ^` \ed 5JB^cX_vaͩcg!׭g#z`r:q{ֲ(fh#yh?jDD0(KRJ+lB 0}trsL6qaB7gʑy7q%@{KJaiVAA%Q坎hAuL*)hՇHIW+-q)~  E U;HYiz3r] LBu~ZLЩs& cfh4{tmA&RT T@[+ qe'<^* Se@G9ϓou摋aVze4 kGk<`N!Q@p?Nzhw,~J y"SSN />b=9;s,c[yn/+Zs78oQqQmC@5.+qQ r 8zɫWtgr{hҨ1Y3OETIN=4)n=y~ }?/N "\֘Lw[G hgA˳P>եB'}2J,WD-{pw{oxNnc -V'xz` `dj/Mx\F[DGjӏf)ZרB@i/Wj%|]hأqY[Y_]:OSO4ڀ1ސgޣq&G]%l@M @ iQ7u7[`46Ԩl7( ǟsӯfg^fKZø]-4wa$ \#:4 y{W?0ae`\Wrw$醣 Uڃ8Y9 >Oo $.O;K o}^%Hw tɑ| >?Q4^;oL3?,?@Dgvf_ɧ9X8R,ҷǬ<3}jYƕkS)zV 8B59\hú(CrM5G A[D]0{Zy"M (UwwP{o[[U\?Jգ/N.J\BI n{1^w^h/hx`*T Wl~Cѷ3 `(+C!⅟-r:(oX-8G@,+E?CBf 5ˑ['_eW~DK-w+Y /,y]lfM*munE.-1Y<ڼpL;=HS"祡MPxt;aHtuoG;2)*W \SUg14{ 3 ҙ;q?ii|fZ{#ɕT{!/Ơll)nPe?Tu?Bbk|F6-DU2fwh=!n4=f#bgwO4GK+ l\GoBNtēzǥgP)' ^QU ivqmy=C5u{`h{;[h{&]`rA4A*A5:=DS ̓e9Wʆfhhi{۪{.9T vOeY@'q܉+>$_x.jEBHUzL2ЄhTD^Bh~(i!W`ʫ)g97$1/"Pz/~msB:=j+=Լ۠j~ɝ^eeWp=o#)Qq0Ίp5tts ) M)F΍)*<uޣ4y%v:Cpk17u? 'hB48cƹ>0;Yvlt*%i[N)>:FV(!Ү0Ev$t8Qȹ$U>Q NDIMx9k7tܷn4nિPQ^C;+sƨŎ/]Ah)Pyid^Y]e/ڻͧ=’d{h-ޤ$ˋa7s"al6p.~ Gy#I+yN@X7;t%W{naazaxcںAO" :n gv޽6ZiLke&}y֔]gU ](P\1w}u .qjerfN~b9?>h8Pe49GEۥ Ek~"x?æq[F=npB>.Z^5? w!XBHEEs\@јD>4dZpj'^xis[QDcgiҸANJQeIj].Vcd>Xq%Bq`N'k9du0wK=7fzOԐDprcN8]s{˞Di3hv&4vb8-jrŠIR|N|=!P%h>WI $e$N:<p)KX2$?iۻ D]rWlu*wôh1&NҡJy|,)&Ki1WS?-E_P$1^M{Wyp#c#lj^XA%'(Pk]+1~AS.w#[0#&Bbb*y)뷗`qG\U,mėn=_*ma\V { #WP~u"ϑ?!YM5^ O'ݪ˲ 4j,!|N†$IIsA#I(ƔȠBCbpzngq49 O_4.pʠcHL4[*<07H CQBnހaW&=&ml.->b| %g)NJH& :ˢ䀪 Q9A@͜Mlh-RqschϦœ mx,'&iE+%ϵd fzYɱ[T|}]/L|z `ݮͷcV8cE{ 80Mh.,o-CfHnc= ̔6Jc_?* S=.+5?_xBT}]Z? =๭ ֞WYc|mX?MycBRa v{XBI 7Lzp76¼l9VHh>[|o Xzc؏_11' ZQL2Ŏ+_OػJ}$M5P.rc1yw킔%ɓ |9t-S̀#M#J#ݑU" I'}KC A,p9"\G:|g02Wߒ!4K_rt^),U_yWYث4q^U ]$A~EcA fBV4dj%I+x ^c]9.(A,jSpmpf9Ce /ln^*J!gE#r`Sq}&V! 6n2 {wCT5\DVzF $*ڒ+FI:#T_ b49~|>K.p "뛮(Aeb72aǝqf/J̪'{\[^U;Z\7au!m=puޟ);TofB^ʜ+[8Iյ|pы̝KV][xFG%ǡS<2n;UIGv[xF BZL&.hSVKI! 0aJ~"3[n *ˮE B(32V) x*nɇ w., 3~L׷R<1hn 楌מf„nglv tzkDx[vAydz<<*PNN<)[}[ڌwSu%mBzzQiKqCYbMu{_\ddz\l4!{!ewxhҥ7924b/kt)݋J-Vy(:l4/W xQ(tGXXS Z,Jp,kHK[{t3xA>o/ ֺ||ψ%6a#;GGkBAڀf8W .?b&$)/[$¤5a;%Wcz04!se&ʰϬ+ H!GȊ"׻3WU*:K5MSya޸'̏f+0 ]PLÒ'H4)?ȉ6nܓ5{d4rUt`@+,9q]_Chƣw\ILo:!5&E,2naV'*et6C(TXm&i6Mf0vS0amFS r-nKK^ ldWZ|s*5>T$+;90kxQ/22HHȐ7 쳥[PYoyMbwY~kU@-Rvq%@CXfNFآ^)y!onM{%.` Jg^F5bڬEBrTv5Dӻ "VX5AnGr&H_muy&,*Hc{P+jo:> Xs7M ~akXክӎ>`'iyAج$"vwliJ6dOKIy)ԹL6NzRmRɟj~Ib_|5; =(kt1 1 P U'EJתc O\g 62F)Cy,oձRކC^ǓiX *nL D]1"x(\Ci+@-PLڡs 'WGQnJtw'bzƑ%EX]$4X.&h8%.k0C"g!⿶ ȸYq8{уh?CG&׵~vAۨL+h!YXѺ<\2?2d*Q!sePݥ`*|^cOߓr~)0E;;ǂq dh3ȊZ&Rwo~˟GHE2l2K>$0Sh-w0նroDTL4_,ގsKi8,bF|o|L"t)J# 'kAN,J˦TԺlMC3)3ʮ~c>RaT w=]=Wڳ?nSL`4\U^"0ܧ)`9*O7bӰ8Bq?%I! ]$hͩlpH ҋV EN8ͿRQ%12!w$䐭a1V[^m v8+U(omW˜0{U8A{v7NnvcUK(b.5yTR_}HLV˸VS8%:s];L66IHlf\\5ɏϹmJ~#F756Lt.Xr''˶<ʏTp1؀nȘ̵aen&|g:kMW%7ėWcQ1HƳ6ӦX2Feqō8v^6+;y!rΘRm&Y0IC / Kn5lB+=$N-_dktp@Ӗ9i ̹ҏӒ ')2С`:- tNoQ>ש,hlTݍ!oX`E=%?7Hz^TD2OB!jB O(vHĮck2ڵ\ǞZe꼊ߘiB#6\O$|K0sԉ1})@t| Z "Qч.6-I؅5^.0&V-:;h1t\Yv4&9E ֖ Vu'~Y'rC>&]*6ͻP;;G^ oǖ4"_`}Ph>/SmPQf.rOKNj˖!d l*|{j>̈́"@?KhՐ8+L쫙Eꐋ+Az'zb}m6;^E }ѽ2{> =XO#!i87kN1F:OgGNl\ŖM6UDiv2T?hwg? R^ # ep=(Y$Sq9LMWdҊ#>U)kȲn7 HЀ>❿]8E\9W=_S4a5!^*W##2Wν31 ?X1'}T;7pjaQt_fxÕklĬln5E_&S0F}+`?HDn4(Dc d‘tz2GB0մs ^<ʝct0c#'v4h^tRÁ?ң6o:WZ"l[Y/);'-DliԜWݱs l-s}AȜ 0쭵cRbdygպ?([1P+lg 8%aKZdS{$KAm[ٿw FpU@?|L0(϶P׆*w]Vpj" 9cS)GC|A:e;d6;#zIuxݭ<*9,vUKIu-Oo!i) ͳwyS! EsT;ͤu)Ж (r=stA< }8rjN3:7x!Eث0Rr̻XJzL_)9L$ X5cľ&8v1)+%BGW{մ"j&O Ț4׵9ǂíüKA;74 _sIJbJ9Cg!D+j=UX]~|D9:9hbuFV=%HBw~J8\[-C;] ޸G@W[}H3QF퉏<vVy; ļHn%>z87eu_s*X# hAڤ}Ō*EMUǤ;IWNewO]'gD)"ؔ`3Pk u6Jt!h]7VCrOʣu@g?̉XMU#ly57`Ҫ^K]d[itg4^ .ĎS综ݬ՞zNDp덈'J3f&%0IyS^vB|dDrL*^q'T?Dtc Qjgc@Z)^,V(gy=ڤd,3ât>|^'6ruJ`X4DY]6@x9G[٣H~|jq''x)!_ !AFR]V;ӴXPz?,}1#|n@PD 6񠵦ZCbbs4ԭ}@#얏h #TLlR #ԖP2֌_a //JQϟ?5Vߥ=(mj p~HGS|Bʳ5Rz7\c>%b|#|zXl0a"9?/l7&)3?L:lDo?՚~vJPI7h'"5:hL.re2`Έ.&wlLڰҦ0Dc&zUw zBRa6(qBo!2 M[ |=9dS p4}TìVc)ssʅ  = TӬ8A^|ɸ2K"~.pzЩ~LeDfi`t4N3 U)F}v9DaoOqd;΅Hܒ|liUF@$Pƙnm4WdBPT YM -bgJG(XS(%?%|z /NI݈Qu$Иl%_d̀NE+>Y ׸7 ?L^ g. 'go3yR[}WI, }{3;#ƷvKeudeϜ+6R,KjBDM3f >aS/OtX.=61}s Jm?~Д4h7h[~iYUrA/5 ֞ >PMV=XDxܠGҍ3"n/_>@%r`4pG<+0I{B/|a*#ǀTDTwL&>mOh"b{')DIO3d-׼۬0*ߍʢ4Ȉ#& P~]Xh_3Y,xs,pVxo!F9 lQV^1Ϗ7cHCbf|W<gsu1\, 3Β:c;Gݢוb7%DwvV^ͅ8E!SWb@8 :zؼ =̥؞ktl9XVQ X0,{u`ZjT)o0n&E?0ͺ;5%^UبZ(\`ȯ\+h0  B3ZL]!,s5m$0I/]b2ctpU(7{5`'Jꄟ?/)q[*#0"9]U,v3S.vKY=ws(q$7 Q$T(`#ܯ9sd H%a DIKAf=mKc߇sSY@TOV;vmu[!, TJV%Hw cx!a_AÇ}(8 j2F2fJԂVU3!J8(N2͞XR ŷPyČኚzϢDx,!\} q|x^ %V TխN%|Q l8S ߜ2b$%z+A_PZk)2($om,}G%'*V:مvu Al%d e%n&;k:nM^!÷;pQ([ iK+DzGxMz1kc ;u2ŲJM#^@%\\m~HaXlvhΏk~{4 V`-YծS(͸NbަZm dpg[Tmz.iO"˰lvHKQtaa&:o>2[iR!HBS|{Tp5U+Kjw2ŖbR0OL̖"{Y #.{C)*[&ଗȻaz6pl@ f`괞LU7*?y Rs"3 S"XfXyEWP &rYm7C>p4snB-t yXfcwYi86 "jJhq (_" n`Z? cWEm-$ߨU P q#ѓ+)DU|ta+wX,Gja(91!r:vmf^]1GIMjUa* ΰt2B'BKBe`Afv-A!NC`[~g\u\W::O*¡׺]t=W}zm"0S0*?@:Xwle) qqmf[{ Rџ>p6ΦrSVIsLl ]ދ7qh,_pm* Ckص]+Ĵ莵$D/׋:*zO)B+ 45Y:t{a?@ ZQVǓע^vQ7~*iHݯy2=2W<]"f҅7nz@B֩,j6$D6| Hsq(7F9CJ,WWny-w1bpA(/0 \Otyq>PVIj-@ G5AC'I+\,zh%'T gߞ/2ߗN2;ÌNL^._B "2#PRjJ˿2_GO3._w@;bQOIqC > l), `xV)Ǥ`!qx-{7kċvr,o7rqs1atbޓEZJ~C+xɹ>Rg[OysdƎ4$_} i#C_ˉ78MG$ 02UcƉB|iՠ5L\}F2TFz :е A z)RI6zSFɺK%]> AS1ɇd5\, }Lr崩+{E%{*ϦP1SKYbOH* --iHPvď"XF%{e[0><5u}mUuDALg> Xf:wȥl.<@+?{-pĹ:["N83UߋEؖLOr2Xh Q`HAUKu*}%Kd?8g-6wPsQsJƹլ{.4j2?tH)39%ȝ<)ꁚsi0\ݱu!c[#ڡ~1aH(ټ!g}<#iWUM3xkuֱ72uKB:QtF{kƴpg\ȮS.VdDgӱ7eB%NcAO9/pX dYl^1*Z8 ?Jt['c ^S⫨s7c 3݄LT[RnSN׹ؽZ\osTL 揽)1 K-4Gr}\qzz~\X! 3I1ލ¸u5E(IA T:C5|k7w[hi'Z6ϓ9nerOэ=YR#J n(XjQ NgdW.I6Q,I)Pu@`J~aA(}0O$Aw{Y$)P^Q5j#m` m!y9l7#ȃnxx YQpKk׃:b̐%.@eA(zsJ3$ B@5xAטW= XvEEE{R i`gV.FOJBoL+#lZIDmqo'RCn֕;b ,*eIȉ+g6WymhNO@ɼ ,-Z/Yj!*'\"z}.tV($ h?AY)BHԏ-kz Pgu8}n=ܔBəZ׷^w(vRћZ&:MlF_$r'O1b=WD/K NP[v5gO{h gZZKr_]^A%y)Lj6X~G"WTF3%Ιȷ$=,Oܵ\ ~SQeR)[b07( ׃Y.N_*(o1 "G[lTUz3иxڥr3خN/i2/?zwftuN 6<{AZBwe[ - Q0EK™W\b!(?Ѝ6?ULZD̑z&<%W7PJ.C}e& ذ715{=5,0>c=.{樓qsi]!f8>^4Vn?Ō)茚%rKB픍\.Bv:/G[@7S=ucvJYE'd&u%VPl1Ӵ@GUm٧~uxPX yRRЃ1&j=y0zTr;,5{@ADdW R To\S%P A^# ,HXwd.0G!v)b&O+Jj{T-՛*Thl=q.9Oli!l[mY;c:$$)D? ObdʝMñK8}l݁ !@+C{Wqhgk:DrW\ZTN8>P_R?gڿY aw}_T0NHJݛ蕟T`e@,VͼNP͐sztJ[v V[)I_ݦK^鏌B-t?,θzc%a tP0[=-ѪAq`4W>K[**, )}ľCK:{g fxdeL{U6je*hm75F!qFSsf8,N/IDv%YO&!39hd&%2Ёђk}/,I b˦  p?A@LdH%~,MU  b"iv% ymrb2`۝v8"sCtBd/lPiPoRES.JUTѼ 88A ܧу@}UxIVZ,ŤƝ,ܩu+Kd#iqw=gM_F5{?6Ⱦ α0"+Oc8]G?4;ΙpUXժbj_\izOSVko{O+A4j P47J,zdۧ#(L3.ꠜ^P8*Z?}Д"sz_e;E 9߆km9V[@Hߩ;|[[ ZDŨwa 19=Q1֎B V[:{ph- &R('joHƣƾ7BKS9h?j N?gy"hZѴLIB۪ ngC>{LLKA{X&a<Q 5SDzP^He-WMQ!t7Q@Woytaӑ0Z ,j`^cqu'ȣl-g/hXEkFN{@ Qy=$,uK"! Xqp1 fo-JTl@ZA/h\$eSCI=IGn N,4SNeb诒NlE}DL$ B;ڼضDs + mDuZ6 +y7;ς)9x\ǟ)6*t3 bG[]%s+Gɽ?FZHP5Zp2Gߑ{bszL U.<~I+p%Ʌ5ڏmiOzGH] 7"}Ƌ*rLlm6lކ+(25X@DlqGB24 wj`OJ+6bjxe`X$9ë'Dh&z7T]0v=i Fh9^ h&]כIbSC"Dֶ%.nJt>If0 q~>hWC!= jfER>omY p[ܦ*]5|x`kdAE44f83QgfoFf&l<,Jɉp~+v_@Dbd $)t7F1e듷OGznMO7*^O84~ÂqK;:Yr3E];[>]-Ϡq\ͫwcNz]"1qKԬsְ bF=} ɇ3?L6.y{\tZŇ{PϢ@oh sp溴Ԟ׺*ek-޹^PǬ.XU־{> F5(-zȅ'^Mr/~]iv[5sqUhG0BҍGuq݃".oZ%L)Xzm\]ӣ╴y]#韂*+>7GDx,>ֱ=dv㸗îňuM 9_Rx2M"Zr4LtH]~ iq3cl yZC~ԎM 5_AfĊׁDj2]i>`>.勍#yʎK@Oc v S__/u׫ISo++햶0D 5@42螊o}r*\JIrx|m]I|xp)$fw)[vFD;@::>e =|gUz5Y6ys\GX>?J0\ݗ.!"{kA*߀kxL)퇰"XQ1gv nrɋJѯk+m?"_r~xYh2^XG+FkD~.j}7M\3_ 6v\Nw!a!4G,Z~X|i7~wNrf8t"4ٜZa0θFfzXYm2k͢80 BK7FmAsO#Xޗ ٝ(t愍t> X<[˃2t NphM֏wA9rD WAY펏mG)sO8O1잽ųL u| ,H#L鬒[^j8>'J5"(i&;Q)8!U)Bh~"gfm礽,x{bf-eBvgŚT~v3 l~–W9>@pns2Ӝh{_DbP%X_ ٠y 4Z۝ oBmK?gZO') pyU{b4کU ZFD#0i!? *[OPm?f·̭5#ÒJw[-1/$g-]*G)-=%`[hdҲAQ˱)9h8C"u]W y|󬈒m#[{>$_~!m>"YȬ5J q>=Xٶn6[i]gpڻ$@X3nҹ$P2h :b?S2*}P_RC@3ܩMhQ2!50=Ά̍@7>}[&Tφ3>P)8Z.W% !`v`%6{\*q k:4&p@}}~!xu6rϬn|V E6͎$=W7=BKaۉ6Ӥ: J r,6coW Qc:Y\8%؈EM?A{.ÊSu:U_(kÛ]@&Pi d{ B2i;9')4չ בפ eY!N-j=^OYϦU$s{ϫZ/i) W9!8iW0+e0!PA꫁wl-+zBω{I GvԕP5EY9+%7]woa+~'bӛaSY @& 6d\8Uȟϐtp;$0.&kء`d>k{0drRU]a”Xh6rMj"=0م~ .Shdҕ|cya闓c8bW"+g5NrB-WQ_ f󭸕HDdHb v7O`@uӹj\^9G՗[1*+>g~fU*'|#fDgV zJ5:CT803N_||ƺLaSҕQρ{72""jQ_)~e|?<oin؞t0H+gcU|o&zxdqXf,$r _VLvc3O/UT%Sp@)<>%} "lA܀sN 9XV` O[*U.{(̣W?|vrPEg`VKڑvp2S3HpU4 h: !Bcϲsvb6q?%+tG6`FSRW.m {eHǞƐb0RX :_?1pT5g! 6cl|R3PV>C2WPhH,ћ &Қ+(nZp9*V2ŘϢќ3׋ȡޚF#m-u_n5K`j*J[w f{)8.CiXڡ=uA47|^@ ) Wq9ܰo3J Eۚw'{Uα09?˜povȍM<\$Ia&F~Ldaۥ֓)0ZǤeeyʙ%b uiC+xe#Eg_l^8%8ؤO]^ 47IT.4șlB ^G&~.S' 2`hmc[9+,'wuk26cJ `\,0uqVO  @+n;n'$-;6bB1`yI=@zs#g,v02 vzW>?i u;ԩݩ+:ǯ!,>$y#|E%|F"1W3lzAA2lAAC]`KGdm>VV >NP*?l>4 wpEf唃Wru҆laS@'o| ѓLCd:\R_ |Wln'Xh&L0W]5\ە q l?vgJ8*G.3Y\a9*b"Uh7/LR K?wbVI20F 6Ge$(E9 ̧YR*R1bd-ƨg`kӘMoC'CnW[sc_Xb~?2&5 s$A^+|r{/"bW#z_w&JSrYxh ]_^e"mJxH^s $.qAP~gR*n I( X䯉o񕦖=,pفuKedkeWpEɔlL`%'77J{ݔN#;GqS ڡE dvf])sl sV϶WU._:Gh~4DPiV4ԏi%^ʁN'~2 —*ݳ^,n7+e{oT~ { =`oMQ)tq5˽K~'僅7'*Q)Ɔ"jKk`3Nx_A啪! O_fxffa~ּjҟ3奾Ńy vwh3)Paq`*5G E:d9kpӣg6߮DEtm6G.+[nTLEeEqtx vxn{z$D>(CIki'}c|ԵN^Ez G˪á vE4:IMGq7^u1st9ݞL .u qR n;FYmQ7‹"UҠL.^#AxOEbN,=cF<|ܧ.L.Y7-Y-mFpˆ(ȗ1oN{5X=%sGHb]E0\8mY{D y# E)hOjH[FuMΝusPT/9;!L :}n{A CMq0GO]IzHcUh9{ [8m#c@yYz5`}d]Mv/@kC׫suDjdp\I>+䠆],[DtdYqdEq0]LLI@Hrhb7L*yolnEW*cXwx ZΫ3Ԯ޾K( B_pFeV4!)aECOԼq}@hi*$6΢?K=COGW+ 53se2ODs&7Zi_ "!ax pV@$n:MŢ%ּu8:~V;Gv? .οuyXp ՚2"e1flZ״?ͮs\{a+Э V ƉVmo@9?EFኾKFpCJ u]i/rָưCqEdM7|[X'wc\4wti95[qQji@Ld9;ЋWjv'33"(!`y&㝂U|u HƸ,= tn9>@I3APj^q5a |bxSAT6-+C&m00?%a}6%e?&bQccdEH4 4@'j2s z,B>\#Z='|)J|E|88E&]z 6A{dT#}MMffAtIdEs oDl}@j"f!u!KD/JvuA0HJ+y$k+it)xCjPyf `f#x7jަ3m gGT($Uf{P9K+rtI!LlG;aSGM}]efU\Z@f-fr?H{cRi,ke5^uũK] nVZm+"bԑ7k^O :d4^-=dZ0O. &le~>F\oT a6@ %z?<ԋFg!:^W,q8PM=u5 7J^ni02NhʉwN$ks B-SI{OX|Gsq#g= -ԥ5#3`~f)aڄкطb٢c8ْ3}H]8bpo |x{dnixDhBGq<α.w,V0XRyE.W[> M8NڲCc@_SI_n_ݫ/CQC6- _xlFKMd {3[T8L=s^}~HBaЀ &S( ה9~kZɋx 1=TޱvQa %4AI7#P#6O SSn4Fy}Ԟa R(FjYE4 Ldz!P1P-AUf"}6%Lt2wKBlT:%jCɛEn:Z)5*ɞ 5)ddNkX6Ƥ{{H:yh@d6\ 80WI%zP7# /][m6c8Cd ֛)ґj L?R8Fi.y$^<)txe MW\~ʚV[ >yZL J$~$ װXGPn\g_|ɗ%=, >NVnF}vB\ur}S*߿C&Br5|vhc bWr\Լ׃jc?o1s[X!-U+).m'Q"e7>CI5j&v-Sj)^ }2WxɝfN]5X!j2"/41cOF~'xg`B H* QJʷLaЀU{6L6 2,+Yv68M~}.j[;d'fVf±/@'x4RB-3h\iN> ٩bym/Ui)c [1yR" qp㯼4kbHpBABFN~#~chKW5F 9$b}Efa&_ARc-Dp~/ޚOt̲6q&trF kDI"dWř_B uo[wnaѣU0ڋ@KCc>daebUOe9 6h` i͵K&\Z/V#S5o~N|!ݼ8ቿl D:yrJ{ö@m'mp 1T?8t)]/@Mӳ84IdzJ+d~g3eG{=;nhpxuOsw[]t6>tVcCדxC1of6znC$rt|W95ybjp8PN;A`j֞ 2clR."?M0rؘf^ 44լC R $HfW ez)T=G\V<c;=S8z$4Ynh[ta?RrogqR_@Rqwg _[߬^ 23~jصGq8K{Ux5Dt- 2 m?3Q" gDZlr_{+ޏh}xZ];َT1@&5bԎ)0b Baz4~JE(O~j4sdY(=!@!yʸ,*(4j7'Ґd8v/ U ,?榴plM!u`H6lj*v9/1hC[˙ 0wӥ3M&)FQC+^-Bb6y&bBD d=fRnYTg]_ &W?4p4l|)DKqk ܮ0nv{3t&lWi:V0H*` +fpCy[^bqbz v'p}U"6$qpѬOUdBna Jݗ ?ÍՏs ?"/(#V55=r>C;-yy{NygNEurHdFz>/. 3O+Nj#aHvO%-Un(wIr͠LҿRG1IjÃwV@֠u&YVPFf iFT=s" 1Rau_RoBPh qKKÛ8|I}7RWTiV<@M~Q'9@ҙP>~_|[̊ ~~mZ-tPE% (h||Kܑ̪#gU:JWkAf1r{`@2$ +1[D^9XU ;#,'v?Q^Ȭd*{?/|^ Sɔrq8|HM0kRx@_z9b~Vk\iB{k8p*j.2&ͦh I6[,vȄBniDKN) /t{򒼅A OVHiyt4=`S2X`n |k,c|me D10Xt+&!RU .0OG)Pͷ&{!$s$ڭ@yv29AӿR]5gʧ _-EP'O). ]~2d6^rz\&]`AGY]ud[=yGhNz2\m7,HR Pw?\)w` x'gTEn͉Ԣ:ت H}|*0CTPH;ǐ@u"BopXhp%EEt5>K+LG)Ő⤕Ⱦ+yHLӾtfj$/As!X .dBQqJ/ah^CFly8/bZR+k`趠<2Q&쟩=HW*(8g܊FX 7Xc~b TLÿM\=YG6e޳}`&pNȯ;gs]Opҡ$ J)ʍ"60/T3G2H/ F pVwaER^*AʜǺ'w{ W3;P~޵h=4 LcCvYLU4V{40˵jI5RX<kʢkSMjq1f@&0 4Ox oYb+Fk%VZ#hڀ"j+vn'AA/`e(uzGcL:uLp6X/5zfh@ ã|^H^oF£; l,3Wj{Z&ʗeMkp-󃄑؉ lQpc(+ tDǣǸ ҫGtOD=Y.\YYNz~Og "5ͿH4KgaBg?t3VM{eUu+k.n %T!TI{JQkrG>;4qιeC_«oQР~‰<{QOj=xP8ezI!/OAQ伫B&쒙%KCQU˧\ 0og|tҾ>#ZJiїZ2!ARRE{`rMK2 LXWv_*S)OQx3o*7H.vKtC\Z]&!Qon삵OP`\;Lʛ h< ӒreT@m}8)lhaq9yqؠw8-|qލM/0,qS|B884LE!{4Mk| WHIXPvx<M܊:dp@*ݗ0: :&|~dJR8?!\k6_FOg:hƥL}YRc̡`-| &,>7SͷBˡؐx9dzI!fL_p)]w)!ge5#G2X`CtR-L+=xc&J;+(FMG!cN1{g K/<ò~D)Ke_uQGQйS:%~< SEZG\/`AG1(W)06c_1²Jⓛc5E3z!Oa4zr0WDXu{>Ɠl 7YT8zFG623W"}H0Bip2rK_os,`ӄW#(k-k!@sҫ$ZEă.U:Ӈ/;&Hx5aC%b.[y4K2d4E2 Fnc1x$qU\$_k_bUݝ[_,^Z'O;iqϟWꮾWUVgv+vÏɬӏ/K1ncaܹH+E*2OߺX%Uq})3T`Kmp:OVƪ46sE]n5&xu+75TF-^cYۭk=V7c+{rf{JbDf߮_XkB|/ k }5̞~ž>$Z7~]YEt) L(:L !X~@< b1[_ ?t,NH8#Wl (b|;q|S}#vn.AQ|4xtx YȖQr~ݒC7YRG\SUuuga5zb~Gg)%5SOp/UYH?{ R?MkA,R3šW/džG &yҌhHw A-%FUzsq)\w:$RTq~4eue&0MvC \+Z0Һ%~u$[.GAѤtE9u어J: vk|I@tӠg/;G(mQ.[g~[+@/{[0D዁ 7}FWhA!$Aw;Q-v&~;MJ$P,dWt|ejSZ b}hD}B5{N~l\U5 *wy>5WgT)RgsZ"Ck."ImoV-ne礕qӒZu3 .;zbMOvenp `<7J8]tn_@p"6m̷0_Ut`9rK#P(LtNj\McygXksY x2o-wLM9ҏa4buK4!v[ ]~\KXU4ٹy7RAʯ,3u*[JMc=^}josD>sJj".wyw`9:4bJA. %˿"2n %%:BI)G>bz0 L?_jO(bˢa>) t]< K&ˠROƝG3ܼpk9N>HdOG"OP_Hhy/XQU] Y>BIzsAWŽFEUh7̪l|+>a`~Ʋ5G&цdxM>'2p='xc,ZGœgbH%Sw59`*Hn4фvG.ylцkݬ+)gЁ=(׉6k>ĬUOV[Q5jހC 5!epLhNcZMUc66AfldfQ7}꜋jюU %83=o0МF(DqB(jz܏2T407ux a7֖r7r7,Kn}[ƒq mwsҽ`s.s=`En$(# wet EVo &kWJ0v{m/ޱ ISzڰ~1ABs.eN)ԭ=Lij+}$-b: Tl ֚ĞTZ/^E瀮LS~?To&=cvgjutBZ9 <6󢴩5HyG%uǴ‹;1o AUk$_cg&pCw_=l;Ik`T}J\jv%rΣҪfs5uDk~^k/r;y9ȬWlǭaىgiDWqgn ,UcBe5-94-̳/h5m X."6gXeBCð~X*;E=CW_3'y%U .E_ᜡZBD9>fkAqsS\Ɗyv/r 2NvVݼ"Qjivf?~n^#!15&B''5AG>D' r2;a٫SPSN4̟P}1 ^M4_)~*+־Cl屌Uag70ۓn"B6e%UI!Q<>Kp`0O)J{Q)%ƍ)]1M9ǰgm>:˟A] e#+y O}-^@vҌK0 Azzu?۟BNJ1󤅇'( :/PYZfb;(:@.XJhl]nxpC&4;3\a%ajUfr;u+ǁD"҇M )_OK#椛f5;(HމQv\ @(D@7zYh,"E74JAdNWqUefݯxA:-Iͩ9~IlXKq+%wlԻ!= Ns|Ԡz 5*J١3RmījahRDКd[^4+DgF:1_W~^ ڕzuBP'@ㆣEX-DeocRU,9X#o^Pu"+󘬹6úGffvd% ˌfO`W^~B4oIhܘ908~hw86vS)sui}zMoYdt X _ v09OݿÞg 쪔j.7:{˜uzDLj{rؾ2 wc$ت\ DM^{C镪NW_zfۈ{VxaOĆU|,9QJ/k;Q~M ӽkBd-%m:- +d9ƌdVm^1nH^xP*:/_(#|Lժ ^X~CO L:˹! mPsB OƪyXC@X1hG:| kzwM0CQ49e 2z)SlWN3R]̇y6e&Iq ̀h9zFY..o(}(;'g.|؂Za#\.|hz6G@QyY7K8S&5Xs1Ν Un60 ,<;dFb@p|`/;Z&5քLV0sG𧿆G.O_H4Mqh6R">n)`梿m=aJ}V'I x*RZD)`ST'x pa:5?V_ow;EUm;8QF)_iDr4NbV ޟ+h4:O#nX.OY"Õ%E8Ԅ[G)* ̭(1lRoLjQ؛#j3lV\]GmHmn >hs<'!0;%Qchz~]x_le@]@5C`P~x\v72;J;q7g! aIfEQˡ))Ŗ$흗-○W"f39b4}-q"v:W.-.G]*2 淔`\//x^V6čًOq IZ; XUTȽb77\خE](T|RF^g!?*o8 0 Jvܫ8U1..a47~~;|ܷQQ=e-0;ҽ.f"]B 2)FQ|忊_4I2Qrų_v"]}{rW\9 )3`@, Q(!"Ѿ.\O;w})E(TFv[zJNF`ӕnGqM`>74Y|^ uZ X[-sAҭp~9 z[ %k)|]Ղ_6EVDlUEO[3;~H\H>v/_737Ϫ/耤% SF҂ws؇/¬Mpo午 BbIXln'Te4یĐBz("B{{^5[DdZr\uC@,XI]qC[ɪ>ڱghkpzEuycXΉ-Jxyx'i(ZC+z!9l1GTFijM;W+( bodpZӴIM@ZjAjwF+᳤ׇ*?J_g] @J|[e!Y.qI˧6έCj tbfm씢}`LɠԍB"F+mvk#&7.PSCWq'}}'Θ~ԍS] fzƕ_~|scE#BV98Z+(pWۤ `V$>x$u4x)b)"qŤEtlJDnf@ӭXeZ{yIBB*zjh0.GQ2RXM"=8JyY3^'PU ]~"83Jcէ0'֪tU]GsKe>SX}aFWR`;uxW=-xPD4:<=N޳}Ԋ\WȒ-f+fSW9H߽f4ݚ7 7s4/-B^t}$;g;NA7+D4ҹ7VF/P|JjK|VXP9(Ä%LUF37 Us($ps~ߨcy,bٍ,n /4FnJ-b5B1R("[Un >, 2CJT(8NmڒH{w|). fMe&mSTkes}jި|AFJNSt=X1.7 ].bAsB흦u~>Owí؛@j{'4tF!o;,8<|-F@,;.ȈBvӤ CJ>16!kxGEsUq.vd%sڂAYϪl_Kqp*ZN쳘iMY_DZBH_$5i~!8% 0='zma&iHhkL m0pA{+BtкZۊI"aqu'@;dۑq]pO? /l[ (`*7*Ja/ɣcg(٫>zZ:G:@DAaI909ڈdܛ~̐/p]ӑO*U|n],Fts@~܌ԍo\9p=YM"m/d6Ls_lv񘾅6mFѭGwUFX(T v:u`‚W]20MbI]rLܘ4 %mۄn`9!&bS?bk /#~`&\ּ]:ۖ|fPyh"s—+7%XDW.8>W\tME6 u/m(D }3 +ڐ !=:1+Նl' eUż$ϲAttz^;S٣rwi$)n1fs=[e\`R#!L$n;ԨH^ry?VkN^Hȯ9/W5vU ETHܔhY@[jdѥsh~HN IxaR!Y8YO!5Bk.x\|vkBؓE>m+K$^ q38gsԳWԌN_SGBE_QI"6~$EՄ)e<:⠝Fs?Ap c<M24j. S^_oA( X=zP<~ Y܋+&.Og UZ b~d2?8Ƃ1R.`0iHe;_$8B{QӀd7&*Z,ʕgl1^ eD1ڽSq>P7i5 x[sIZMrY\(τ|xV]j,bMaz9U{oM|u0P7m AP ei_OSo4L+ϐ֓F!K+Mzor.}0&!YEMtKѬ>gZt6m&aZ{=ɂiQ]ܰ >Vzc_e"uB~Va ckHGk4r IɈe!kP;^W&kct}1n3LkN04^켪=_{H/:s/fW{)Ԗ v.h?SECb_4Y޾Oocwk2/=h;˦ p{Mhl=4+N:)}XssV) 8}@ =f4 `h칾 o9t0Ku1ZG'!\z7bI[1ܐyT(ƅu$B(#vߔ^Mhm2J GGTL$,dV~Hw̙w[(15':勳)TUj;)9yxDz1K+28gDFW9Ey[3\8D)kM&Xgıѐpaά,VL DM-#j]XiblGkoh_E?=뽱Z|Ъaymg$v} $?*he$&]]}h1}fṁ/zcni2۪L[3?F9t8٩jLc]N&0A#"u$ >Ttk }wdVޡk;* I=+L%`M/OLuM,rKVAd#uyPmb\cw]2Mfw"֛vڎ{Qyd(gkIXR3gSb~vW< 8 YۆLcXdnoMlf{ ENq@i陀Kٻ|}z|x ֪Z7<6 2j(AŷߏWWv$Z/rn.ŇدV?1= ˣ0^޶.3;p(Mx!49s'^P&kY:|gʽ@S吒24 xgMdnʮYρpajbY A?Hur4KJC; ?T:)F&x9m 8E;au))QV/kp TEjg+~P;ѕ$B kW ; 4-wOٖF0,Z}%4(E$@#4Yfbk=u)}&ƀc?45u|Z/ſd?uU!Gs*b.s+@5~mvw)vr`TVkXI׺s#oet?/Q-K^D{lIHkts8O[CNVCP3F҃N: @ ו=6J} WLvteCOI" [!nS}RcYZ"dV@4N+ʿĽ0nOܪ#A2 -"T]=]aKۥGR iI;W$@ߙtd%)CZA|esxZ,`֠m%)Z`B'3H#=EdoPl/YE,T9*}q޲xZ(SF[e s$IΎhH* 'ed\3 YRRl+ H-#豺M\}r'8q9!~:|;wNpͅ:ʮK4/])GQ28PsSbU]MHrz#Z06RID ݟ7*lgx3'VI 8;jCu V_s'j AW@6I mvkfQ_xy`䃕_OdX.팑q^'ZLb:s(3|} G\d7NIJeg⠐:bFO5MDy6Mb=i6{W,ܴp&1߾6X)u&Sy '9ȔxҘ43P,Iiۄ X睡ݏ}ދ wk9➅Mpv)y$sl.@Z,HMҷ+'D#0wSPwg_{iygϮɳٖ-]jF䱻¼#ɜU7́QLT񬤌El `|96(.p@2F4Լrco^7g9rͯ 7Jye[ v7wX^xSzz@8AXŊ ۏ5UZIz lj͒УO錥c5'7(#V`|,PȅfVPS%KEN+ Ol;t5E߸ Fٲ.ţGKitA̓ Ei/Xq."fXLnLt^U "Ee0Krcw yRd!pbв YՈ{K޺#"墪mҍ|*K }LIѱSTP+ml\mI7fAy@,-,pzftIEFMC*D̗ ٧xUɫ[6cEB:uՑ:h3M!Y%xhQ]h]$sTG=πcgx)lc8yv HB(=̠*x&,2*ǖ ؃V`XAs/ >g MYt"wM3rO"XeRNE8&n]meos;ȺM责V;(k0+V6!tq40d.OE=68sto~uPլ @.9آ>\xZl4مH, M&tLl8A&Fphw=z$B{%EƵԩӒD[d*(zd5䢞Sb5Q1E|xi~k,{tdvrG5I\x J>;w*j#m͏#vq{N_@P˝܌n{>3t2xx0_I߶`$em+xװo>~z e2бO젗%Y7i-CBQb59W?ooތ&*q4Tf7!Ož* *k/qڽ%kWJ-FoVd>|vu+~@:7T:sʼ-=iw7BN:*tJ~7M:tbee*0Kt|@B{>j;;rf70t|jC6_p;ө60gBؘx,e^$a{H,2=~,2ry@ S9sQbZ–N?}!&řd0w_"fwfb;Tq"r,^[G_@Z e0 'ҟSHRe!D* ~hRo,cDx ìᜲI{=YD_yP0u jڕ>%TFԾVjb_RYȉ Vr5r}dkgII{~b28K#1~Vl$=A"$ p|oaGf9-6Ѥ.uhw{$RyT&q%"k~fg"*vԦE=Mٞq{yC9DRfO#P4dn H xҚΌzcq?F'W&K+Gkï/uzl{,)@@4PǔQ3l5,<8f)isB D\aݑT\̨p9CɬrH|f]Wtǯt9{fk[K;/[9 } ė xNV{9sO' T㬼&7KLIbqJQ9.߂~QY~y@ n_ꕸ*Ӥޔ a]ʸU(٪տPFo\Ihy9qXYp9f+_|os*{jEiiĞ1yh(0f6U?KzC04~XbqTexԄa?FIՉKJTGX#J4xzphYDKVJv?q?I{c˚ S(kXuvEIjG6p!YjнIfJ"TX܋RaF8ZK e5?Pv]1omy52n,s6P8 d3띮NAx R/3x2>==}$^9k1o[q.NiY) AMyǒ_@i^磰=602ÀobS*eJՉνy4.kdc 7r :$QR%KL|" :I.(ʠi+|w:\`},KdҊC@z^o}oIgVג?t+.wֹ*RbI/{ǫZ[kD{ll,r?$|գ9 0*O촺YdȲpEUffp b3m!- iie/*Jp'tK.4q:ja;4x&icv0HW's)?G SekBk*$~U_YO I3"'pp}o>#v^|]W%tg؜\MA8ڋ?M hyI(HZn 毊t19d_6\V)a;|,PO99`GA(5@ J:ҡ {QGYԞQ$2D*!i@B?LB$2BŮ]BD@;=xm9[q1;Q+I{ALwAxen(. 9Ë*aL+F 7nGKaˇ* C_h]GDVoffYbEFZ5]]vห4yUJY S.)Iqs+xɦ\[[NB ;p?{d5KRM $u4b5h\m=P]y0`O@0dfp-Vh7X2BBbt-OI XH@AM6/.KJ[\%b*968oP'_Pt*釠v(ꤓćP )Y3pUP5(xe!\`L39bXk_s|5H:=QTNbYD\Ⱦւ4@9{Om|ꊉ{ h+e-f :C`%䆫Cr8J^F@(ϠE&drX"JK]'[@gI8Kqv,nW< tWi&]E ~ώc\E;u1^&^cKZY!\[x4O5y\+rPe[E 7܀BH/ FjlqI-$hJVO񴺜Yy3f#)(є?|Mznu-"F=IMxyFNqa&E1f:/aT"% ٨%k/A*@/Q5ߙ{k.R ^spҞ ̕JpfuN!:>-$hx=gUr}򞰭s~DYζ$ϤH~(ЂVՈfP\ n R!*oA]TOeK̗W@GVC#]'=-G[Ҏ'2<0T+ xY ~Kqlz, l#З֡բPk\LqSrL*,hoh"Ot)Ћ:J^擂8;$1=w3>#'Xӥh 4`*ql5:S˔7'ӌVAU?.&@*0ec3 ۅx AEȁ^zxTbj4aܕ%c8έp쁞:oP_cdHg-AwɱWN(VI{d`lE /E&JY| /g~9XQ _$s|Ȉrc=mODRi7vJiJѻ\iyabts[kA/c^J]5lIДwy"qeێ?~xW|4vb%WCLƝc_E-e EPԕIhOkeH\P weQeզTS ԡ1U&Ʌl[i3"|Im0 h좢0JJ{=(e$ڞEԑI9kB)Ѭ8 DM bd#˕ڶx]N\ΚQzc0]%/n!:n^6Ş3NOU1.d0y?t6f/X, :XuVa>(?Cx bRc6[\kD6]c&1\&յȱ ?9US`uðͣ $nEa.VY^1qș_;:H{g\V=۲;ȗ\|$|U1tRj.P`1酮F%E2#TϨ\SAu˄KN` ]~ li6uH˟3QMe[`5PG1NBD;W\üapWP"XىjU &YE 1Cxy:AJ-7qXb5"Lqj<xÏM, o?R87Avot\@dSy@Eͧ*i곟@97ws_=xR"ΐJcЗ^7z2xciz+7 +xFXBn[|cep?y^QEZ+ߙPX/ƁSǮar?xta̞m}V/>W/lBy~ř{o9IU_ sCDK!w^EO ϗ , ׎"] TR^s:;푥f3l !ZyVZ+.Qw98"!6)$65IAcrL[~;^1E8|]k# aSJt\e瀜n-#BJ_L%XTl@,w58$'󀄜LaOO2, kL 0w~[%Q#RqexЏ%Fg,ʤlƖZEɅ$Â>r\MIJC/n7o>V^M3U]˩Ee83`_n DݝQ%A4~$0Yz{Z:{sYڔ'j$=PmIpS0ꋭ`҅ތF|wplxc;k6sp O/2g\>lcCe8)?M#׀35|(B=^]dCϥR`)iQX qb+{x/k_L'2KsW{:DNG/ahHy@~e 7;q A.s=sHxa۷l"qW"9`t1Ijf_.ll=e`:ƹجI `5 z36lJlFN6U2L p9ľQ< S9 *df =D$˕Ypԗ3NQ\Eʺy'v۳!_ޗsMI܆\[C+,N)Öb^#a4Jԗݛwc01ΌZ\ d" "5<<# G7 /zi8E&(dqKq Hi&a:D|k' A 6k&x8HE8 wotm09)0Y|1D79ta=*iL $cjA~>u\W'-"_ .Y>cQh}4ofąu?1 EJݼ`g1(]nh=P|'8xM>̛ Ɵ_@p%^"]r^jJ>W/)y\)z&UR w :O^Bs,iҿ+cN몣 `xxZGfBXzI Jj`N4i(}:[TC!?%'[WLE'^u?&0l^icUsSjv/la7 zn'sGIL"`3B[v2 D{*@4Af{l+8RAWw\C&*S2AYD`$9 y.%rZ[*)}{pqQ\k%:,F"/4O$2W:kV)$ TK9E*C#kG) ,ob_oԜRi*hjt U\% Zzf]rLA5_RڳgN7"ޡecZOAY;`W54m tgw>ݢKm+Wb-t"ηN ^G6S]szoUG捻"|wpP%TK\CeD#ͽY.d.x%WUi۸ 0}&pShp/sg`\ :KL .ifdowbivM2}6+F,03P!eMu/jBǤ(_Zp/#VaW?Uh)6j}5va>C ($_FjYK΢<o:ȡI/zI^#VwR Fιc.+7-0f'_YZ4;*^S5Y^إ"_!Sh`p^&- K6E? X35I7GQXEgsdPv6ggQ˩ۻjN` :Akbar!s6ydͿXU3/_0E@&U ]ͼI+qQ3Z}$?6q}AC"I M t{A9fY)9?C2CL{NA zK)lQ2neVGMaIU|^Hp.(uGKVKU%,c{J٫P^3yN7މ:?s+Mm&CRc,%SJ@$S:rCVX[lګp`|j l\Ҍwy'E+06‡C-h W?X)ZHNŦ5&;B"WBEjT 4"dѕ0;sw2uVnxx> ;V2/pO` Iy=t |/5&IKcC+7{lG"PJ$gݖ|&}WvlF1JzC5Lc/9!Ŝ9fY_,myu3fU(sY>Cz< mg%"2@U 8ukI̵ G>a Eve.$</H B&h)At2a<" )^E/IalL&8 $ߊgyzU|pfܬ8 a)r5ҢrM6EQᝂCѿ`C*p4G̀ƳzgX`}):l[Υ [k-TLfXQPSl,¶UHe--PK|O :64} &TbjuQswfN~ m.h%(cu˷׼CI4 (]VڼK,_6"L\i/dgZxg׍U~JE-yfcП)3=>Є̏Ν:,=7.^<c/:D3`hw{~Ω奼w.}ycјxp-쓌UxǥPK὏b X~P_7ٻco1jN hRYZ {vߕu8AJ:>ܵjM 7JZ( G| ~WbɃN)Y'XiZC;CjԬA^^2.ɳjZTx1R.{N7蠶PgŰPq X~{U*6?h3> 3|xl[y8ĎIQbGѼ} T-{YB錒8<矎 q]u3qp\>B @FJKզL .HJ]+a. DS/X3Y`ϾY.מc*Lf+ Xv~\⟏pі6G5L߇֩4xv=6?6 lΚX\̲ 52,ۨpP+*J8Y;*f,g@[烃- Gd`cgIJ K)z R]$L3Ԫ|]t&K*Nē`Ph-0 /so\ bFHw{w$mARbN\IN ~Hp^YTp`G 7ptfT>'d<^y?l-JLUkcU.&\ e~Iǰ 4=+X3:!Mrai F@ʤ1KR4M#^U@NʼSZS*Z}>4dsPsg4z,/XEԴ_XrU6L$bBDjl j5dQ<~F=dբH VX Ü `$Fs (0G<ċm7r(n<˃G#v$u<[:?~1FyR4o<-&Qna?Bh[~yvY7,O(8Y侻&ˏS|?͜oJ5;`kX4`Q4fP!yGo&70YTUoE۞S|M)@ڝG iڏ j. `?KmwT/NO1@R&H-f6/WHR#*t1!qXڏ4vB ?.*xrhB"Mޘj|*pc%yFr1z^ll-г>S\H'7X\ l>¬0.+JdU*FjJ 0a9-6s '&U!\b{/#.MySAk|DܕEtIVS'f/i}_d]?Ҡj{+hP&4:ì`tlE)xc¯Su?SM5+q @O2ՆG;ٱB˼\=xFMWq5Ȑc?&-Wk]A@ u;Eޖtw{A('pBNFﱍ\u5H™Z5R@K-< KN[3R!PmJ;NUgdH_?Y);SDžR͉^f]8z+ 87λROTGZVuk[I_RVC yFmGf 8. KsٸQ4_h w(Pc\;cuȿEõaFz)X 9؍Ͽ )ֆ^M_zs}J u0_"v}W_AxtFe.ݰnfI~q6BvQGh3[,  }*"5nk |v䭺yh!DzTNiӼD3?bQ缾4 " <{sPN4q>Y)}!R2ΪWbN 6e]v<>PƿpůgҼ}u OI皑O_%2-U&NZPnB2cXnnU">amMWͤi8@[EL]'|moˏ&~[= ܆IG qu@SnMx/[鹻)4a5n-Xt/ .iV^%7 uis6WyM4L%QP ?E=W ٚPv)U&OIF@T ?4=786[)bү8p$S\YEum''Z;FQMv \T  MUG4+;^}F$C6Dg6}qCœf@A utOFsS>-߼ Tw& .;lKS38% bpϠ=+%O$[ 𡹓 N,&vӢ{43tÄ.K~VxY|TyfIי'Y2O>ZbQo3&DwkA4 47@811,,)(PЄ -$X%hcTFd7Ɋ3hYb mCl6Zg>WM3 m55/Y(auNnc9_Ȼ7`1N*"d(H5J8VS(./A i-Vv+m!E2*Ə{&yYCkH۔FlZE7*÷R'nK)$RЬxX9uYGOt8ڏdr⦅Cr6mX0zZ{KZT!wf'G^4*|R=m\Zhdu mH_g1hR^jGv:P$dK\`4p9zbk.}@v< RN=SsLщ @@K[lA54ʡsdlX^9C1l"Qb|`GOܞZaΓvCY6/bfbw`+/l=vOu)S2i9{Ort7."ɒ>0=No8}2 %} Ac:vdhᲐdEkȑ{q);)5B**Is团4asVk=# ~AĜ! f9#o&ϾmRP@]GMŗ^'m]{jNJId0NgN\*jܖuKH_k4|kYHO~d=~_ U+mb gj)}ɼM37і=^R1zZܡTE jPeMpVWd}\uUQwc2HrrDCQH(6N| l  ahJOͳCk ؕ ٪}M$?$6%6`_YQ~q]̍1*( /q$@d|lp{@P΄`:`OxDyw0<*6tr^`sʀIG|xaiUzeo@1 T̄B1&p1 \^Wڨt_E 0INUͯ>r@S氛]쑩BYq*QTe1vX[fh0~(.94McY -sd{N|`O|%+/kN9o{zB } TQ`ߟȊass:]1O~uzHEnB(7X6#- Hq%M CնlJ䵜ɘ%/ J6[4tVTkz$9&Tpj]#l{ hCV*۪|݉ -#lD.e}Y;a|kSѭ|Hgw=kn`w,5ێ=gx~͵akuvxszdXr}v{gAhBe CcUX6ӡO )!9(4~n u?#۝DFdrrbr 5HNC/cs;ޑ_y (t }ڌ+-PG, ]%bE:lG]t.kFc&Qȡ+.2J5dJD;mapܣ٦7Zw*5,Xu>|ߩJSң8CBg㠫U{F;C?k<*ozby8C^Ȑ `U*ȱK3bSYa5{ \&Ԝ k} pCfDr=Jy4qU¤Pi9P}i6{j [Ύְ XE9 j{yDPs&#?6tS"M~aCj#65a=ٿu^dG+ S#f-x[֌Q8I [>^IRC'9N#9ʱ>;"`@$Q4e?6<]"'.@hM7ӣ+TZQ%kk@9zj"cHkjM6YI&_ߵ#m 7Կ>NeQB^4J:>>TČLŖ׫@")%֟҉Azi3"Jp.]= Rr]k [ܖyc5.#+)0Pn#c"=ﶦ)09UÍQJ~qo Y&{ )?%ߩOk%aYj^ejjqW+^ 7G\?DP͇m(}gPf|𴰼'U-gXUZL4Kbx zXQ}V-;ݧz'ni(jGE/X!at9hKgkNsqC le+QYi8w³!; cq@ca~=%3"ӓ|Er"̑򵻂rNq ڍC? +誐-< CDst@)/fW޵i~\arHicWA5!LELǕRe5YP4C~0vp0iYVpdGlbڦoaYjDA*-`z{@"͚'ٱ Ά392-l8tL5!GlxN ~ZqX/mŮqt)Z,26S'~GP6ᗐsKhn24L9!Eм8}(XS63 Ok~D2Q5 &`{ɪI`D,M\g|~ Dd-IrhTO$7Y>B$kZ13U}u2ex6ڌV1Tή`u\i!rXf>|} iP ^7hYWk8e2gjBOePV"ㅒrV|h.lnfKR_`=m'J5IADh RhIP:4mn0/vG|Y^THI#W4zĹ)2"O[N35ѾƒCqoD^\*ٽM a,*dK&J]Jc&䋘8:#@.̃s63.vqح:̼>j߻"Ul-/onv${2obr)1{CE&Hî{%Z$6p2]#hb, Ck'W+݊K5G^e La#Cˬ) / V*5}&X}gx E3IcL)RzEL86]&&erXHJۍ1/[|?; 9.0Zb݅ŀHl A(=I(#hF\Ϻ3)˴yqI-JԶ+ﰓ(4dwz~2cyޱ{lSEe(i!?ԙ VQX)e!(Z%h3dƄ )RLWsE$"QEHNzFChYG=trOXKǬ7&b&y)vHLX+LĪ*N`aATKpT iv]ur PϩSmzJbDX!a](}҉LeKV4?3}BT 9խwW jx#nNQd)&}s[wELO{kc(Wx.w5rWh*SIy3o|6D Dg(7E8[S~" M )ׅP& iDtkk(Fx7+oON'p2P29 ^6ZA+9s8D[@-7<)B9pB6p#4e3) Ǖ~er0"X&|t}hۻ9w:8*,ͻL<6R110Rc *vpv^drnp1X߰wV.o]&Xp΄j&9au =ӀbDO@措Hy&Sya6?Mvj կ㉢[`CуGRO$ELU &Jn2K_LY ڱ1kZǜL~vȋ'DMJE!o͆1妥U=APgR?"j H1 M͏^|5ivo(-*vBS  c(2H!z ͠&rRɝA+\7w frVWno {" Øsr Cv$h]D9Pm'Їi5+Oll~iT?,UkΑ!4|7d1b ɳF2vSXHIDcOU^) R'?c4eC̟P#|8Rt~*:Z 0[\ 40nܾ5.pK"iڮ{ug{q{*4#`':iL?U%Ki܏t\%ES\ӓqm{L҆OƥPL֚o4[7xp+ n!yÕTY"\rV.<HO$=C.yYU'l 'Rf0L&acw9 s1k֗.p @`&o[;"zJ\f" 'zW´daDG߄G*ݎe zhQump($qP-KM!؈KLw IQe1LkW% ^q1(LPJ^hݣ !b3> sPZʪ܊]<˕F4NռP/K`Gc{N35_T¦a`/>)v Ve 8;{.2ư(!L, _mD!NH-6 qEGx@Fs1t퉚 $6OrGjȍox `$8 Lդ魘&*aOf-KX!xѲsC^"2!rG uyb.&Ji喳*씾܎U IBP G*d._dM*DZn<}qDi9_5u˸x=HrQz@v=xNN6&tVyڭ|h)DxK7>#R>@0G ,7H' H`ʬrW:MӯV^ZZi?XvBf6ېlàDZ9B9HI)\g_N+n-hʐa$ ^ES(UQG50}B6ucr,☇ldMKm{+<xܢ+"'زXՄU8~QeM w2:qܲ? Fl܄cF:F4PA>pQP( Sd3d{Ax>C%ښHTp#ތI?bM_} n9 :e|&~]Kk C&T3v=H{qI>slEL[/+%,aqH#jjPTr #S_ 1W'ql}_$&Wt&5Xe7xT=|SE-hƘ&rc(VyDꍏ(-KAZw jK}1#ݏ(N~FAI';ʖ;yR+#ܣ6t hfq&7R+c$Pɡ o uDkd@)=D全9d.'4V\m{ j%l3>[ҸPv Ԩ5ATn9V:LWrRo.UÛ2K31Y*'.eH$XEk=ueVWD6-ct:oюSMkΗY/8naHwWuha\GP~SyY;Tәtkە 6Ӡ=c9nd7G(a|n0B_f$U7 v%7Pt%"^nB A[J}l5wvw=s WBl6Y1 zk(SYCn+懶'ELi<',‰E"@ $ƧA!A맵*ꋻR_1u^Y!Jv%Wr듭l R)i]%sX՛ /ܣ *O0SUK3>Z1kHLʎuTJB*׋U {S JsUqyhѴWH+¸PؑhU6m?J5J9R)CmfCmp'fwK]"y%uϕf]Yf'3}jw,0CMcW(ҥ{AsQT??Gs\/D'䦠r NbF>?` %~衶feÓa>_F /7}7Smrelb0oI#K'&ŒО֨@2Ac 9X+#Ռ541,[5?:^=ZOBȣ Y1/13ӣgo҃J3FJ$$lmB[2Owkzr bdJ .|pxfQE+ߤf4 iNb" jY351j)YE^H̺?uY;ZNU;+m429gGҫ ҳw6/?%%x#S\\.,qg |&^P٬ywC1:t_ß*[9[sI-/ݯE+X%ũً)k:VHAȸ9hE8@q\*S&X>(崑*aQ:T2)z?s{ĵ3%0kQ)8 VhsXŭEN nzYu[z5pQ5zVӍRL$r:]`x抙)ZvTK=G´IBMK_r,Xr AvxT[hI|AYbH!zW`|fD"/T+Kk_pχHbci.bpތV_l8-d4,z{A>'70o1u2ߏP;},Aiw\d*E|r`\N.H[Ի<p?c(< f@H2h|V@x8k_$1sUdJ+WUѣnUAbl4`GA;=gͅOb9ƎvLA|񳨖r51"S")'nH~iW]2[kC1M kN@RD7Y1e\4"+3&jI3i:QѩqëKLQHMr)2Ee@AߊSs'kPX-RY ks\8pGW۵: $k' * zh$g<)~dq:b!lf rL#'ӏ]9Vm2B,iAC=FtiOvRYUDÎr1i/% ̧q10-;(R@˹D<ɿD&6I3 rv7 pqQdr]uS>wjc b5v~UeRO* ]!YgWл}Hu%$طBowjdR$emkchzFl3WPi۪ҫn+0Ȳ,B!כ+O]U%`q_sGCt @BvKYR9J5uNvOQ%/F!AFwuK0=gY!&Ӝڛs^ZYn> i=vNV2ٰTe)i>Ґy0l>mNfOHc}Q¿xRia]>|k"S<:ƈGzdT³W&a^%r~!P4Sb]"oCz:4C.9UTŏ`r.;$Z `v lG'D.ZN=tccuE亂!i:V3ߗG~ZVqQ90w2pI>,#n&3(bݪEΤGe!!Jn%4lwV/i# I:=QpOMyP=U%5يN`gʌ|*:HX0?dB |$:ee9wMꭿ6cDpS#'{e̻hv걾02*An4~Aݪ^<.%sʴ댢u^P3z,zrş猀9& ɛ8`*01u8n zBWMx~Gů=K ssTfWo&e T6X{'eZDC[Zr^cT5%Rީ(&w}m G3bПNGOz~x)]h#7C̙ƮJ/|O$+Ǖʱ+ƹ i[( ];;6 0]b$ 2o.W)_ ,ڐ!@X38vnn]tyLg_MŔ7Jv@b[=?PƲ~Y*ӍPuM$hԟc: ru(H:Jɼ~PbWfdTQڣhOQ$M#gUjrtru_s&%a6 ]jM\4Zʸͫ d0ۼO4Iw '9"+sot]υ>_9$e֦3,ׂ0X7Ldד?;_\UzAN#HT|ML=C=G,`([W2rqDŽȏqTS17sPlH+ AdFvλե:ȷ9&~1[_r]e+m5:ݿe& n])ٞՅ6p4Vvƹ͘ 5*̹`rQpY9#)̵`4?ėT-{BziA2s6qyDUgCXXC] _w2k b t+b ujRO2Z ۺ|i Wԝ.A6P *쩢MÜ dy !ND$ &jCf VC들$eR9%^v?fX]^Ȼm80PN'7N Ϋ"cTCYnmiE [+. :18UlqzTE`Hur$'0 _3jV0O>sHv0w t (7XѿzhRe4@=H&f-릉1,V~Q>c ^g $NMm DiNrCN.MU. б==!|9p^, >(mI&(KOd/W7Xq jGXk Ae~[3٭HK##Hz3Ԟ<;e$ڛmk/+YdG[c蓻b;ޘ%gGpdlVF Ip ېnZnIn0 O!n4@{AO @kS#P0`cM2(ΑwH?Q+RWݳX@ІKT7(B 4k}f\ȯE8OZ6KVv+S+g=A79st-ЀyA@v@ V5[}"<8pE ,4W"ew8`tdi$0uv*Cs/a'Iܭ^bFed>O}i5qOe)!> `v[R6Sؔ_Rע<CZ'a}%>m4&DptjX qs"F蜦 K>peGXݰ¨0ː$lX8*X KqS@np6F)g~Hw?uЮ7Y.% 3WuU( ,iH x ~f'ds!AQ?_>~8H^N/i!JIg0R>3{eJ e(YŜMR'oEI&R%C.![.nTy"c rAlo<53 N 81tXv<.RKy*w5xRhڼ6꼸Y;Ih 5M~v=y S繵lK~ϵL;& N{$2\ \l4SYs \h/$ t`򯫾x6ww_jQLL0gi~pS$_rtRٿp\\dJ&@\k=.4fŤ*C/Gsk+XyH72JFe?m&ϼ%OHdAu;0m_EW:gY+~aA{Xʤ'$9, ?GnZ'{<4, nN}m{WH Q8~4zH-_>!Uann@@[udޥKULV,SМ@җҦY)29ހ~t{2_eLRثP mgz(!G&X R𶚼1.!rsu)kV`Oėd?~yFrsDp U%VaȎ{Zh  ))MDU6(,}yarTM^9-Cg/Jƫ')' D5pYзT#2Ct!6L`ϼh(R&+GǏƢ*x` lf{TKx! #c)0̳}k>c ;3!PhUڞoG*05OU#cgsA_w"H!e}O?OEҦ_H#k UmY!0g #>2ZJ%/DN-#Kk%ko9$ntRd%m2swkzE@q? m^h6j/I,%OW5C[!z,8s uKOkZ _Wkt3\42yqćyo@Y$B#zN[1% h+80Yi_YNebIC.mɃSUk{|`̭Ha)Le@/<6M8&I j _J"0kUYO4AE6l?f7ÿ^B|Gkv6 łOZsә, Qr=E*~9M(Wi?4#Y#:뷭LW^GHf Q-w_[{4(1}JSf3Mu43֏9콟6nN9L M)TQ(%hTW_è=N+}KZC,7N:~J ͪI0NbQFbNhݵh< -a|}<Рʴ! X?M7,+BiO`!g>t)cq@jE3%> c=@Ui˟f°I-(tZ&$\)[GX!zdmzZpT8imxDaY{}705 Ewbyn7f a㣅rH`GZwA ON&*]":cxaYҏr!(bCasۖC~'p#g̐S5c0DL\=E:U&/$"(wGn3rG1o <&ٍw5mk锯V@+̵[ A KjKާ9;r,؛ i>k M1 +Hj)夲2/`Zڮ!l9Pe2s=(D녲Wy]'e|2! pIw02߰7~ZMzT^q57 (K3yf>cƨz_;' M`tʑ; r!rljk N::>w&-!1׸A -WPѸD(7X&DSgiI>h ۫2Y.,n/W~X * -q Jˑj$mWSrAp7|ٳpAq O@dB|i%w@LCjkXi69Y]iwE|.J8G)@*H|]%y) F:#z,L tm>V-֤"3>y!ZCO4{=W/K83& BBCU_(~ Hk6wEk|3zWo_y7߇C%`y )Vv &Z})hHp0gD U29@Zw qyBs{XEܯl|9ҫTSemE*P/^ؼrEY\p (őjT:WmӍW#NahzP>^qTQvRc,&5.)A%sHC[Gl]1n-xld@#+h>9Hz]jRK-pۡgejE/#!`UwJ H=_6#; ^*-hM 1TwqU._qDOQEe02Y V><<dzOXT CDi2R>ղ^9 \̠(EβYH;E6ds߈yR-{Cc7쨚/b=( |[oqiGK&ȭ9Г9ؐ QuKxGZV͂-[}Ƶh(`h3/vORv#fFEMjJԃ.>~4Xm)6KQ-s4Җw la1TQ"hHtw亪0Fy.94tRBt+y a5 HQb B[VD:GpKMNRx'[?m1@--oP>GBhĐ6cV8TtO H^6]fbb}B/ʴב'w# FUiB ].?ԹB=ƙ[ `r̛TN.(} _0[>uآWqhur]aPK BΙ'} s3;!=bYoE[m]&׵Wǂ=s19f:O@Ec9dP3g!]~WVc(HNMpf;A3R9KZ[{l󑯚\%P,' ]škEpI(BAXÞ c]%xaybq-TgAm*Z,K<;j<򴶱a_)Rl yC$ :JN2㊈=k՘I#6,fPy'ΏIwqÈ30}}Lc)1Qm>@f 3L', p΂#ARj5E?k6Nhnm';K7ѫMYtW5okN'|V '3;V) ygo5>ƚehvN ׋40/Xz%:f9-t 4o:)4 +Z'ퟢZSV8cPڴOͤ礑 ,^0ݑtF+ވ)r+YEܹzݺ29L}E$N)e]DY|BjT_,"ԋm槯}>BMˡN),|]=\tѯqG.v,,6dDBtOqڎP%,CTVū \ԚŇLN1`ﳟ>帖[u k+ y% V M~Y[a':i~ɳ<ǣUڨs܇Ef5}xAV,%d߁ߚ'9hBvz$ܒ'r&9[;ERPqcoۥD e)PpA%j~x'@yxo T[̰DWM؍G9L @W]fZUF۷2}>notMoƃӼ]}E_r`w{{mRk5|?t>TRrGXU>2m5Ӹij"Jvd ~⹚p#_@ˁzFxvx+mow exQ 3w/UAKP.d"t4`@JxWerYuGOxQ7$g~W988MRVck›J]V޳,雧q 墝96g"rN3VExɖmĉXۤ`iB'wF# zTa,U %eRйߣUU77xC-o|JQeч\ah~5 G'c@/ s[o.[0\AXɍnCGZȗ` ,@4'dm<!$&ctqBNeāiۙv^ws/hn\DVUśJdM\Hs+CuiD!3OrIkL5[ ssg02Ǝ}Al663*tIɮBZ{b i/@G)ds1y0b|kBtά[|64iB=Z'yc79t8cK7&>Q|霅StFy8ܱ>&m>Yt2KpVǗ-+o,ō!뱁rM<C 69ZzZG* Uu}*`ˮ4;Ip5vk(1Os"O"UDCz!VEȹYa\#m#%=0쓓̈́E>;٘J*7GMh'KK;XH,d7NӠKh@B96wV~џY(lmWm'=a;~fP|sw:HQF"^jK;ܩAf8QsZ2ktAD *$4#ڬȭRl29 hΧŋGhoGú@(W WZN9Xy8^DSX,+"V6$EhDϽ0> Xw `D58OJjJӾ1}Bang>v#a@;69G>QlBTL(T$7y/{L ZsjRlTb#۝)f,n!@Z$׎ Gl'r!yzU|8P0-,G0pb".fl[R:@sKOD c&R $ng*d?hYJ߇:,{ aA@姊cKok$q:-%"}th7wZ3 #Y,^}ԣTV =Vk8J:Y"yf;.հ BB6#% {чC]ٝ3 &Ly͑7ӈgq(b,XX!yr!2=Σ1ܶ=؏XS"!r,Wjr1O=VTAte[5 ,v\ Nc+Xchjs^e#K^6IO61횥v' ցC8aLK!3qpubA|8;4k)`,b]Lzt07 BMpΈ  <>gz3ZdE2Ж\h8;tₘ>%A ?5%GȤ~>&d=;A0G _8A4M@=J~~%"a+=bE}0M=nt$B>)&|/)$}ӻdžQ< 3Mak !`N[b I)Ԏ-LQkx(&)<^K;te: U܏@C b ϠMaãyo;I~p:Ԋ N,) L{%R*ڐ|+ak#:f4b&15vB$w_߲Tp  F$WQԿoɋ%yǸrplڨU9\<ȹ?q,!r^M6Ҝ<ޜI=XcrN7_8ZrԤb,=~ Ԙ}#BXhA0`'ɴƹH=S.@YӪ8@tI&$RFH"@ W5.͘!1\ 4mV[\Xy2KjP qnv(W<%DB+:LRFL!QJPy cMRJUׄPr28F1u7)lU!E|ӊ‹$&:*Ww ?ʄ F%İ]565ߔ,2Fz5o<|MRlۜ3Bf;Ș{CW}qc_EOu}̎0tU.XGK{ʦhrh`Yڷ4&1OKh}A\}rf^7fe ϓf]O"$4(doО@c$s5DR .B89^qnjRt& bwv@PbsZ#}c+k SpG/orNicklP86*V¬1IT&ePa&i J6;2nG2L\jЗ,ڧ[Fayq?]zR $ 8R3۝\<#OKשl4Ԅu <OCgΕJʵE;j)㨏n8,}˥B'm#p s*_^X㱛,f}9=RSJARx}_8$V c{0Hhd:#d<:E2,sg47sm3RJ9Xq ;PM_'ں5o L2w S9w}R[6# _vLpDF ^"84(UwE;\g-Ca'u҆D.Pf7Zr0t#L񠢕%ʡIhN`,qʱה˜d03(:"id| 9.q Vss^ܥ+O9@T5qNGCbqfUKj*#5׌.W,ttQF x6wAbd2wqra-Geu׆7#QIa󉱮+ I˛NV%dYwP# fѥ7z5/b/'dSOH>yK 5P%ĿܦwYy×?i.8ѳ lL?D#jhIc<= 7m:Symɇ&K@w %\tK7gm8Q]#ؐ@ifڅU[b/`ئqr =H̎֊ w9/5rxC=} =/;dP"~L`7?~\g-HLP(U)7)e%"x Uv!X. =r#F*Cu bi>(]ÊpK鏂.|,h ^j̓mȞ/LmP@wOvИ2ڒqۭ'05 0!-K\X׏W`tH$+_۫h@h&0Qa-oko2)T MC{ Q+xzɩ0.." k2G#/(MŢ:]&p"[I*Kbfe4pa0V xF&;+DP.j7X+,1g5DՈXVOǵ)#xfIE톺R.}u7F'ɽlqR;c|[_ի'2nŽRAB*xʄ#%7qֳ xF\Dx 5ﮧ gOdڲ=^J˹3q#Xg;7G r.OCMm, JSv;ŷgaWm0Ν<9#_#b<*%wúRn_"S$MͬK:dPtQ耠j֐UWV.7N *]ɶ~Bp$x/]5"TV;i^C-s!_sFz#Y+|'.qbJ Nd5?68T"w:}UNÃf. _e/M%4J\c 4IWR{Eؖ\',֍Tx;#8GbA1i6ecZ4i?>x{yU* w\yPQo7ˉ 3@ko~O*riƺ;T3(+;wRyVdl4O:u(!&z(IMol3.σ\+ +e5uQ+1{ީ;7HI3hs5PX31Գu gR"Zx^)6y?PKisFIT 퉶Y0)rJRW,NA4{:&9yZ<{n7!Q :'7aYMEyUU~ >,nX8Ɗ$CMpUQՅ~oS؎dtXpA)å9ùGt AjEhl"DʡN%^ FZM_;ɋ:)^ 4(4\%Jepᝥ}tq%*,lħy* I!8Yj/8Kճ03HrG(%ٰGLw x Bݦ04#*-*mI3Go,(HD< ewelˆi7/ɑzzd@[op >s`8 {zuL̗ZR BPg?}^%4\`* 7Ezы7xX1l*8gn w 4Kâj3ЧGJOԾkb -ozU5A5OYncu V FVjMo /%bV*z a Ws˘N$vgi֛ܻe i_WݙIjQ_KR+Bq̦.g#iH הHPg)`X4&(}]ATdQ)yѨ,'Y=>W4"᫛M (ԙ0PXzf}QԄu3+dTHՖdR,{`ZO%^2N 8 /76h4bǣ`FT.κ aYĂD?h=냆[ѫ( M{Nq7|8CvC_Q,Dc#K,68(=/R-tڀa.y#RLZ[{pA.D !գSژh"aSKƻVmƽj/ɾ|b^BVt|4tFƍas@meP.y*p wk/ t FJ'(ǖj_'T"9o'YK;/P;pkYqّ)|8ԛ K[IJزsr [upWuכ" ,4M<_-y$+t>~6lH[4/8b1ZԗKg0󨞑 [ _kR&FQfzzɒ %>5؅2e@UJC8)LiY?5Wo63b a|^iIE&q) uɄPiO Zw`C$JJ{9ӷt-5=hQR֢k۟۳-A\dƸ4 J!P@D+e 6O!-T6j2a!>qC|VLcKV8RO:6VwQYtˮ6XBsdu3 U].FqCL5;毨G)9}h !56^7y M(?WY|J!L}ϫi9L9?C;; ^(ީ۵fO6 |'{Ab^h4C:3pUAR&( e*=ưkxp[9Pu T}o1Tdvr\*KBaū7}w6 J]\`ĭ8ZfAF!)YRL1D:\mE61s3ϓ9%@㝇TS<`/kgtes8UefON# c1ϛ~Vf+B78:@T`ₐ!$y#$o*+]=f<{{.̓>}W Ÿ3L㍼=DYhϭ@30ER 9;U"BN{ӹDbD"(&4|516kRBX޼{˪5)V'd 3UFHigXy#f>[]^n16础Dcl:RoPiзʩt.+DZM=jQq8a|xn첌aML?pcxIO9GqN]z{vO/`?SEpEzI[A +$fv> YIZ_'ŽSv7̬|J 83$꽚SDH,@S!_eUL@mm kev9͒%8[/mzCPuPNpqd+U-fj_ hX7H+-"Uͷ6NЗj9Sȉ2v~2[8cs5 TòN:o6ʫ3$e'aLF=bK.-{s=iQ+u9&~<-O"wG:nxE44G!ͥԝxR>#-ȥxkCOY 9otdqXzerc#%n}Ѵ 譻%<&Sn|߽4WS>nfיVA4S4911fq5 uT,"ХaY]Һ& ӴgO@⺅ʃ9 KccA?3m)pOGg3S W4 j`F_vg`v5TF@]Ṵ aU"&ūݓ2wIaO~cN뤄7s ?^;i>Y;V1δ|tv#@O:'@d2,2z`7DXgI @y躽&dP|^!"i&`Nzv[iҩ(zIF9ȌhuOE23GFV8|;,>$ {+^Ao\U2\}ewzYx@R2sչ6"@ӗT|$WQ(n՚@d#, ^=Uچ"zlEK⑽__ࢉVTQ}vSk>^D&4T:7 LJpXAmQ#knwfC w#MUT_zN(hG 4*:>ɩd}P9aUfٯˆ; S_UK@pۧpF=owc<])*λ- JPDR.MAA@owy8)WTS3^Co?Dr0K-!Upf귶# ] ZDGsbjyvIwt74\b"`AP5;lte<0@ছ["ښ|Y7s\~Hg&Z-fG/ ?" VWl L^j iT,?tph@ehኇ𭏠뽯=vu.[wManaACmS=1lvC(َoofV8~|G`!)#A|mr3N~8jBV/1 ~RS3}Wbmm{&n!%C):{(2eC|R;y;XaT\e҂@ s)YҚ',+ H[ޙU3\FPN̓n"?DM=z]hI ~Y`>FH nz}hKG&nFX~f4/.¿h] aM-Ù 8Z?)@;,w O>AtlhMl*"@o;hhH )`Pq?|Z% @IF=,7UL{5<lS*9M}_ |c{]'w#g %R&Bp?1DH,1:/Wa+[9ٲ n!JׇRzDj-~@P #5qΫnMoF^뾤QRC"br#a稧M>5n2eUy ZC^ ܖez_J+<軔#jJ$=4TrP}Xj!W X٭TUurdV0;7l9;(z+=&7஗@~i{}z2k_G=YP=ͯd)O0 uՏ6WQЊ~Lخ?&L-$a\ vԝ8Ѫ>8g{\cSF::iZ.k&fT 5ꋞ#]NG9\BcA|Q-]M-#n\KHytZR n)$BT;rƣ޾*jֿKKnj|:Y̨ 6<ov'Ct% Cieu{Jʷ)ᗪ3!4{^wu&ܽ6'ޭkaI&X.l:F`Q[iUJ U'$λxEyvZ/@@RHsRyҜGq'GB}l"Ag2MJ0(@ Sa0%O|hW(s8J=#dPhi 9߈ŠOTo aʑ5 n-* aůٵ SyCJDDcPBsxTg}s(w>T%@h̢ >v{%ZW<S%2s5&ޜ'L]+uArC5~ff^R`80˓x߬T8h`7N&kʗ=*_h0vzU\bOi4N )t^|gGXQ/$65d6FWSOJѐ80{<3'U.'&k2|7 SmDs)Ӫ}Wܳ 0_4'r]*WL0YeYQq+w@uM(²Uáe̔VǯD83iMҒL-̪;~Aݝ=CH^"obiB(3q'}seF~NA?a!]D V"U ~2'Hf>4Ig*/u> /Q"\8M)~ztKϞJMpTø5!NTa񍭂K#3 ̵9 7IqHBx)߻f$Z$lF3|q;-=Kl>Bhk\c]6U&Fj~D+QY sIr⯭Ur}Ă>)åU$% ×VZOcPauzs ﴵI/ vnyFMnNDaq>LEv?d_p- F bq$a5ߪ<|p}BtBq+]槝N"Ͳ_@ 0jڑcRR=S3dE ^=fIt;_z,4,\ѿXqM۪vaoG1C1u`Da8lcjK) 7c4{*ձ$~\PPoo\M?8X EJOLWsyJ8^_t5 enxiOV{q>(ߓVXdKg8{)P\lȅn]_LJ=IL#0p,'|DǙDSH^-)DFH.")PR=lkr9id=Yl$1 (OETbXY& vpgME TXVE}곻P{(ev>k )Xt*&QTC^^CV7@Vã3/ݚ}!4̥`s?J1t rIEJn"1^]:vlO?Zpp͉oqdk15Z$6#i 5›:$LmٌPI=V[*{ ҃[`9`@ܮUpR249\iYQ dpprAT6>U@*}Jywd6ꎟt([sZ =bM [:8amPSURW3]J[םc{`cPjs  odEj죺sz0UJm/N~;qb'Ȝ\AxC[5IzG?KyK&©zX "2Q@B@3"Y:m"EjW;\)Pt邛e귖  `P7PQnֳIVEoa_^;u@}WhkId< ǑFB}_([+gmjwԍS@x5`bChүXQggگhe+uK._,Zʮ\k_i!E1PveMtkɶӨ&J-u xǜqź6`4[܅.a9pxcӑ/[޸PE'rb3M4)7|9,hpo0S d:supʼn_eLF5,آK UNƁY†u>H"/ʖhż_XQ={*u䙂= pnCIF#P4̀iHm+єkYaJfmi­Z}<=Pip2#l]2xh[,CxӘLHi5YbH߳֔wx7~/GrІ\/ig4ZV/7g5vkn9Dr .8w' %2*t,|&VۂP@+_QZ?i:fvmܢfTR,tE`Uhl3Y|tJkaCr^5I?|V)V[VL񛸟q#0[VoNA14^,V+uꙔ+HO6\t4C<( B_=ha$ZBD= 397kq8٬ FfU /3+qxUu}r+;{=wB Vڵ|H1MDNR?- }7I_!Ayp+[x*tP.byiwֻޘ)4nNz=2rm/`^Pܽ]N{EوW--uXVC"0E=pdgMWk*1̐24)奄εY\5YAH!//|;Ts4ay/X묮\dMe.OgX]|[c'{~{MSHu&J+E0D9{Q̮tб٠9@HňaV2{}'@Z2]lEn|Cп84[7Gm<2-U+E!@ ?Ӫ$ͦU%q@Eb̢Xz$Žn^fU7e p K100*}F򎭭)TkRj,+%OMWx/TYw5%O,}m2K3b9R*]0klE~?9E3Ud $_DTPyq|/TL23z(1%.E)ۓ!G/ BXKeSZN6Wޘ3-vfkⴇ{Y |wqs4njxE 4M$,h^@I<T7Bӛ3A[)D+wqt0,d|嫡~!I}[] scվ<,Y)lu,7vNQ©`d\'+>b`d5SuG )#l_\CH"k5/0]v-gO\ǺϳQOՠᖭfDڑ8H=q_*ca)+Xܬx'GOyW@$Ɲe?7'X٭y`>˶UP.,iBA8KhFvLܳXMduk:eY+X9l8p",IY!˞g>VG9AUуD$0>#4aʢHP(Bў`8,[]o&/8T&n7*&InOt.+%rdzQM,/,[7['kLjB Q %bKDXX6yijdXܬ֘j?*u17q34 G"#t+: ]]E.]y,O{XRx2wTsC[HŎxU>7iH#bȟDRVBf'Rކ&<=JU{ҏ;!l\iW(!fC,nLl{G ԥ=F?v5 x;w/.:y(8޹qT͞]cU(oS(6[$޾XK%Z#F Z > 8h$4 滣E?B;"d3rr|53A{lWd<+I˜k~cH$? (my6C_JvZ/nKo*vE%g`׬UՅlIޟ:$ tӫn&K9hɚ#wvVW^[L1|iSsjFJ5b< 8uk QܲzSsNMP)󶊚sɏ(Ph6¹wsF.<ޟ+ ͚h7+ %pA"wSD$W[㱔~W,tn@Dtr;c#mnl{`=t1ios3hVfR0S刮]|+رhG%F{z#N6p]a;9:xWpwRM>a lS$gHUoߓX^\No$9~99,OSkL?8T]bتp7܅U*d"5jH${4ѯTKd&R<f Z~#zF]&QS[![?`ūf{a4rJtJu0Y+OH ,6ʢ,G{Sq=BI棝Fyt3s&_MnT)C,ѭ^赧C7]~1a&;O ,)%ouɁ{. #Kv=He\BP$~$d+ϴ$0|9.eOXJ2Yoƀح4Z \U\ &RYRh Y wP+sIJh`5rNa#&ĊiejmJKLo(;2 Ջ4{H7ԇo_$5X|)i"H,8);WHק7X].W$gp! RִSS Lg[~ :>r$saeiLM/lʹo5]LUW!U:l,ft?L)l{z>'W#ʺz ; hzٶȮs*DT6.>!4@UXj!XGH_Q_Jn>R eugYxMiTY1 !)+1Opz-9.6 λ 2Ll46V9ѻ/ᑑhivu"?qQ%5"4."o[ĵou] a}`] 'CbQ6T9RrYfsّc˅߃P U_U>sr;'I #i9H"1u tV 8 dfnS:U.TηB/CB G$ qzc{ȭb ]z"ZHtev)xJq:z'2v^لI] +d7wĘӾWVmLd nfq- klRQ %\\Pe>HJ+"G" ; d2ɵ:yݽNm˛fla̭QweWNƝ!xK@baUj J[+Ǩ}j{_HfgB62NJ6/APݦ{e5Wp6rag$U, C;6V=]>CnnayAhfqV7Ng&{n6*LXoꌼw {Pw8wB֦T>2' qY.9`I1}oV|| 5=OkY7&RW lKcJ7'kp5%su?)AM˖--|m#En?usvF!롐;ڒq hA}^0K!O>D>C W'FǣRˆ GZD[6ɮO(:vK@Y֗l _Y&,&oF=A V `ve\JŴT7y˝(r%] UOSTCd,#2 M6]R 4)~U)Q)7R]RVY摣7`$alIUM/En.9sa0lZ_<F#/1e#lz0YN,L4[8:0 ;fKs_.1PMS@<(v$ jVڅjˈ+i {w(I+5%9Kcj|%7 *nNqFtp̛EEd NiM;b-/OBJ%E6mپ6U,~6*3fBϣT)/v+MB:|(enR 8'C#/OpvcӎTu$Ѩm6"6u7]ݷr U8fm6͘G@NdƽP-;:3 $?QpxbjOo'M9|]E"ǴB5i1I,ŇjtNӖԵ //pIjBdԂO_,2G rPxFq~ =]Sf*p̻-񪠊'2O3HJ8:2>i?ł t@ENԁruiq:遼l#4t.vûw FkA@dw1o+`3H"\yo@34|/JIv<,kh:jN%˪Cܵol{8V~5BA5;y |Nq!|Ns3,g(yub w{[ui%WS"x`p4 )LUD5 7'xY眀;W0jGH.=cqHqRmg.@ύlȤba3i72JaZ3Wz`*ߑ@/X[@e.]k}fsLȼwy .)xg!IzB/cg8H̡ff}=\7NOYbl3}}3O`߮b9g4yBrf8_KzQ%נInAI'3w}"6hx89iCm)`<' !>,2# + d\(-\,| S[dDJ(cH;ڰK;뜿A48WQ/H?O4-}\QGCc > IjBV m'<LWu Q6l%lnE,DCpWBT:ƽ߰ɓJ|ch%<4?%썦p$~z{v+0 !&Vt"W c֥5[mlj%0jff%R%r{.`ۉ9lTRi>z"7qBzr&DSa1Vh)(D”OI&Yf Ly x__29xՕژJ]YT!q}6̘%<=G:hr)*Eҩ7~8&Yr8`vFn~8'e!sIiB¸ZPZ+ J+ ݫbR:WOZ*n #&ҹqvP[r?LiG?Z[czҖO%=Ĩh;?Ug%B,UUuZBkORaXt0B kBPDU7S6&6{8cشBM'}=" H.MC-h|MEBFZK1ژw뼳'!B#pqwa_[tY1o(9ފ*'f_^>l>vODHI]BC0RS;#SVCz;,A\Ja6Ǎ)ں6F'$g>1 7Ps$-:񿊱 )Zۚ)z'Mq7 a65m+Ueq3ʹJF(+?&?v9wE/M,WDye@;%8+#aoQNU+B~G& Lrsk5+`]gn:K3xFn챃W/K]󦈹d.[QnVq:!yS& N2>SjgG.o9`Mӯrh?.cp`ɣYzwK  JyNR'hIHE:UGe=Rwvc 4K?;IE'O(G?].L|̞0s& ,9rcWַ5eҭxesBץ6ȀM~ECLꂳh4+v:)nu[O}?;OBl'j=YA}.&!+yˌCQ82>G%SzahqGA,ƞۇP GğΙ67jʾL˫"utѭeYUJXI]󵙉ODS2$@|TA ˖I+8ls*#|۹ 6绤&f;\=qA+8vuFRAMd_$Km]뷱XNh X;7V>3䘵_|؞5lGm y-78yuMg=pJ^=.5CPD) U+jDJ4/q*I0|j,|[Ɇ=e, z<5w -E/v1oq~RkǾݯؘ N{*6 "9Cb~v%ٽk?`zHIdf1(F!|p)_ k>1H߰+Kۦ[ hrCX/}R>v\\^Qhʪ>g}f\ڔO[>s Tf%xhC$ s"x,b^l䗺DP:]~W G_#'3M6D<17P?9v|Gr.3}00wRؗ{IL9~Un 64JU×kHbizkLvKhKpa^$ sew';>WNHՃQy\睕,4>ȫ%$5{фE==;`B2 >TÚ8Dߒ4o*=ױ;:E$alb|FfX3>6Mq,Omip6u b^nޤiCDK]K.⺹L'BA641C|fgψ4q9XAد[o0[Ӗ7_$(_APC[u\U{w^銂81ތsZg&Y {ό楒@ϝmuS)^ Y% h ,@d)lGBl`\[sRlph ӳZE>VȘvsf'Bڰp;-fyV`xu]&N IhObZ-6Feb] yICd]'4 y^ޗ fMKwK8!4Wicwi97M e>9=Ԩ<7 x;nnA&. p;nuW|>Q8Hl>cr7*@@bCw/=/B7rоn] /z)ЛWw糏`N} StjYioWE[ =^L*sJ@4wܾ ^GYvV£ ?DsɈ:S6?w31j߿XCנ1.NՎPLի j 5Аv"^gϼ p'9PW|_Z(juUAEq~n*J1֑|80 չshI8 }Ħ0<}4` ,9aGd]*߱N381,jd*ʨSmvXx-˿\Ѵl$n/8 6TYCö簖[c98 85^^7!Igb7p'y6 0֑y7X=!*7ȣ_Tu.3:`dDŶc*/(O؁@ \k3B A 4\}@H/ҭ 93V7lՑ?DL>Fk3*STc1:ݼX+!piy&8&`ta4qoj.?Tj ɨd%TєgPl!k{\cК@C2i,ȣo )t;r_Sao˽ĥ"Ld#.$%S$貄(hdiAO@ EK%{No0h' I^RLQC;+|uS*>P]` P0@Lͦ⦯ɢ=2m8Ne|w $ *3|s4Tp ,%2+r{q{3>7%JW$~zs L=.Wlkijೋ9O08 ?KAET#` 81 "+vhe}K{SgҬoёʮf .w.H4fr 5sȃ0\R⫥w$Oй' x[usjC'4Cn"DX4y p{VG\Cv׋^p$34lC2Dau[2E0Z)ۭz$Yw.k؉UwTX(‹̤ժJ'j,9DZG )gϑBB} z&e_ڛ:NQ~2PlA[~yQ OXU33_GۣMQzH=%oUia*x]!߇b{zk k3 뇻 nc3ʺ^fx%[䶀p|#ˬ^lQ6Fىjfsf*D~Zta}?)ZUk+ܧ=IWeYoy[%v2T_:}4矀]mM=+ I{! 4gOӹ!FeRkzy-U;x/lv;}ՑX^fd5T)ێΥK*IΫ~.x0ߖ|,\z]fifli,#N/ԓ |ܰ5JrMtM*7@䠃`~_lZq|Gj^lt/OFՁaE,$D յg%IOBOZp/7r^VQ'!CaO劭I+ʎ׫Xwa"q`|Y}"ʇ&t÷A>F>r\4}4WvHlhb> К9YﭣWAdB-[F @~!-}fb.WQ6pRaaG@F}b`$/km"u&E>.BR*:H>)(J^XW#TzDCG7c޼Ӻ uM& Q\szĹUybB?_L :%_`K\"aH4G~w3~mf ?3%+t' ;A&(Kc*}}'/% MX=gXn3#l?w!:MP~ll뮧.LxH4ŕM却L#hBЬ>"Ŏ1)pn.r8H_0uXUzOq"^jQ5 G-L!džEqMY:֘Ԥ6 ȋ<dס?dA1M6pPi @x3Y+\7NM6G}ݖ ݞs̚yɏ>?U"P:Wۊ6jb9Yd ^ι.;u/鹔\clZȨrG52H1`+V|A+[*&\Μ&TrאjylSÙ`h쩹Cɠ_FEn̎!T$wK@u{"em֕eMMcUv͑QmIΒ2n3"'h9^\â5p l +& .UC]@/IV8VH^.lgDpFʳ\=  U^8-LC8G3>A2:"3pUq۴]k1N(e@Fb~/pPSvnQ3SfeQ3J(x7lFw:;H"Z(͖=xQ6i~ ߬F652e\տG -vk7}抾TB,aZYҪV zE s.{C)ZLJ)V-E%m7֭ jD\| L&SO?1C#!}A0o6d9gۣ6, řsh>M{B0޹e&FïIU2=0'ò[bh8Xv5!APQ*y =ũ'Yd#MwW275e?J?!),Ap_%qKiFc4XDʮl &#-N8ϒI2AB5cA4\RQs4Q*.Jg/ξWPN0$0Cz"8Rj u56 \Ϟ!sTɍt3u61'RS? ?<|f9 4㻧g m|. WKB'`&=a(HlgD0lE;mŽ?8fl jv϶~ (e>x&v |V8!",k"=!'b𘘉aܽD 5obVĤrkjtt'Ǎ1i…ʿ\Rqt|i`)=ܱ*!EP^BJ6jB֔VR,==leǵSFKЏԠk|S^vmZUu?qqlޓfW9j"/n]Mqt}/Cm6֏ce=üzek(4yITleLcAH&!"͸gʮ'[00n;H>'^SLV |Pm f1|"M_]!Vp/m`|DWқdL vZ%U5bȋ:U~1SѲY++#zr}ۉCX0ϛMKLjwⷿKTC9eME%\UEYStdVaNd}FPI2\Mx \FЉ8tC13cu=˟حHU&G\OHsJ8"IC?_T iw yeMՎX!+ozV$5y1NrX״6g%,9 ~ f^Q 9ɐƬp?&W;V(ڰ= /B*2I+kFPi@E*m0Ne*:(zQ Yme (*BQ:F:o/d݂stᜮ`8`Ny{ܔ!ngp]7!zělmSgהx%p33PҊkLXELbGvLLԴ^QNd R҈hj-URGTo[ 4s.te2 b CeۼMƇDH6mPpLko3a8Zj?".]UGJ&\`NDՆq{d1`4\֠I'7aih+ 2>ͦ31{e lfW~e0'Qé1IpHEMH \1^3BV oDh_ӳAk rmYBEUrž~#טTeCUg۲3sz- *^A6xv(Z+F\"R8l[eЎJQ6E,xˠ &FH-Ǐ/H]y&\` +S_sͿaCl9NE{^'!_r'R.ض357" lƵ3JQ_{\=)fi!w@O$1[dM-Hgv6ݱVm?2Pl(u}xUR6-SSH+¡0ڟ~lc#$M܀?LBFҒoاkAi Wzүhrdbv$<$qFFh٧[<ԓ$02dss Yt6Qnwmsx᜘x!= j,T K1xi0b=*'7zqy1Xkv*Kt+½ho|yt?l]mxO"ڻdV"9$} mY6k'Q@ܛpK=ÂR'ys EO)jtbi""|9ת![lԻDקj*/'(׭t xi ;~τMˁ..&m1PUqkZGmRh rA8̯?@}7292HjHn*/ΥU=!qƕ03EwZ=we <G]~c/:Ifs2#_%~o.$e␔G4AEvb1 )򗼦 '7)MM,o n5I4yYBJ|T7Q% 7P7$.N#2('SFD 'f:Hk֣ &J] $3?)t4D^7V;:ܤg8<%ɸ5}O hK/Or;W5`vQsCн?[" ldL[="Ӳ^猚H<7$q'gכ3mdUH gO c%i# |4jhkϩ`ց5xM^T2`]++]d9n0-߄>v"ܒFT)^!1CVZm\ J68O>FyE'x)6q9eU4SsJy 5 +@埩Zo# s@{[ewٟ~TCA{V&;ޤo{?@Xzn>"ns'(E簯sp/ 6ፖe ⛅cWK op:ћwRĨk惞D묝a%FR1YQSgR_`k6(ͳ5ބsKHEIIeWU2Qohʠ)*B/:mMH4rGQ7NiQ/Χ`6sǖ؁?h_2Q]$a׶S#d`zW̙^*^cwMNmI$B%+N p5k2U 2c V 7bHܟH9ݻ*J i>V:3*o^Z~`B^;8`O5|$}ɀbE- BM`}{Ǜ: MԒR:|f˶X%푱 (9IUC?Ոc6)W/$+r Odub#[5ROl~aB 0:᷾$8cힰz;HBeO"]Fsg`suo ߨaw!pjyʳۦ&[v;r+!}Nt*Ip{xE;bgooSe<е6$G_5##"sew_wFqRM?I Lj'=FbB k_j1 \ohذ"!qZtiv<L9(Ct,PЗHIEyV%}n 뚘WR@kPt [I %uF<7h(b|xk+Q-¨[\OQz |[VfqǴfAt.>>tĠqy)PͅLYm˝h}< o$h2冡n U!> 6+=N©7^ #XXzB4 ˭t)M_ E40z~[@ļFO$==)e}cŃ.\S1 S"bH(xWX@,~IZ7=qc|*U2I 'g~'v(]:vİy> oW>X"e >aƅ_v;th(侫~`#f:Mz{ƳrOaf97R{5c$Ql+-⅁[H߀@=}lzIPu-x|qy(gzV ni$aOUbi'2?BVK*6m_gǚ /צ(qeZCɥl[kl݆lb" θTyXRäq:v9OsQJ3K%{^dG> .I:z( fҋtkkm?c@Bg5C.PV3^Gd"?%%궂 Iʇ o'!Z.!\&^f`l,Cf ۓxu_{N4W)cOA)I/9Pd U5QN FUOS\]hRbx g<ǸZ\~=;D(ă XqSdړ΋Nb# uxg}W_lǖkh8c1;N=k̏%!o A`GH a1RE-Ea=6 wKe# łW#^G~S W{UynWxMd ҧ TVpÅUi8䪪]1V-X4uC=ʾ*cICC)"Aҋ'hu]AmtQW(SNXA \>"E\8ZW3b!ƪ7-T {Eb!&:}\3h,Yc.Ue!9:|Wj(tie10#BzD/[W/lEDv1m@^]N^=”Y#q:SN@%%Ke ˏ˧U}I0h}aA&Q6V/MW\_,2u7c3nSDTToGyz^ċ\De@1x~;tǰ~z;UݦG*=r#۔tf\~j);Cl Qw: wx $&?;bȃĮ$(QV~j=*8oS4[*eUKLviM . :rkAu5V-oU 0l<*jhQl+gӥRZÙ=|t%SR[a&@#XĖ_GajX2d\@mRHTƘ#UCGwE6N$!qAg|SXԼNFH|O0~Jsoyu4^gTںrB32=8S'T$drRlWN]\'I BtTӦHV.춳[+ w[ BF-Wz]!c>VyfZVqk4yR{PIjsbi/唟r3 軱9qۦ|'FRhˏAX@V=Tc#K z~ RNG^V0WϊlŹIP$clgક*.TDo\UY6q J`d^^nX8qFmPfpryyU0Oq.>?Wlq<)p Mle@n=>78K#kRT9M>NȄ7]PM ʂ1UH>?2!&:^;%(~Nk}&CNЀágPKns:xO/Dr;T5Hso5~++ťq1H?aAbk7 y7~KAd ;;Fe(|7e)(@ICjBTS|ZV|1ڭNN.P2Qbm8nӰ( k۽Mqd;veq*3w@I"Ir^+ä~6+}Z0~3sJ7` d sQp7IVnHM,j+]ؓ=Cu0/ ڨ\n#`9ssWŷ"<ܥFo/͖h AweD$j{~ey0Eitx]O $R$2 f6/e}c4sbd3\L 7uX ]HP̵d8m!uU򋂰F,8(`\!f?Q%#t=sjkEݑ`ݪFtpehsa,|,8/TOӺ<+QG0nZړtv>TJ3BBgHY{sXGX)0ň*s1/(4:Yf|G]ւ@|Gv0 ,t)2>>* Z53mu[,I#_Kw Nޅ2P$\tDmVQTj)oEʩp$uG?.E=Ts%#Ӆe~SDE${&(-$@pkѵ&fޞKy)9t8IuK3'=PWna1aLhv/q10ёU1X ;R}~HTH7$0l#Zנp7 c^zW@yd){MR6{^epz|'bAu ̶L0}YT;EG(D$:PCơ:8XbvļX; v|nz՝wIB/O7:~,IKxlTL@t>;WPM;CMX\t8I,mް{pUy P 'co뭖WS6:B ! \ Ynb .IlywDdmsΈ:>2b>dֻ$W9ϹpL}7)C+dUd{%e̅6LrxA)g62~M=MUژX#SM o98#CH/)"2H̱W|PI9곆hwmT n6OӫbCaF7 ƹLȍEC!(>+KDv9 ߐ(>;^Ȗc^Χ p1aYٜ~R6SUFj>4yu=_rE?-z5J1~q<|PP3XT=oAc+-*c$`QcY$"sc{p8~YAɰ3ٺtL+35 HޤD:t*=/NDVVL*bop1$bjvz(jo/Ψ?WZwQBfԝ`{Է‡g*\ILAIoπ !8hT[I(uRbZ!V%@%m="LeIe8֣n2: !%:_7="-U6h[?ϸȦeDJ;95 ;NLH`F0de$[C۾ym؋h`|VZkF^TmPuExUk0J3"St/%cgUPuu f;-*zKxro~xB#M);(|feŰF:1b,ڊT+H:$W#jt+@GmQW/1.u:M~|wYbԽK$IQ*z]{ [ĻoRc~'+G1s?:i: ouoZ.b%Bd_*`B& O[&D-sπyOSHP4'l5˰ O ~cyQ x{ [!"dزO\i?76Na&d`4uA8=|F`1 Af[nb_g@61V]3ǩt w~΁\ <3LXsꗦsL$6cnb+<;3ֲ,JߴTyF)2K==dLo'#)edQ38F!nb 04{sϟɄE<^ia6&FG|{aa39  gv?]9an-PQs Ӹ*%wZp3ɹ\vR}z&.;X"j0S~z+ۢ \ ٠ot=dt_m4?iPsoY2d:iP3П(eF[5{Ό TDb̕NA6p7j,.P qXq*\ d>Xf0dp4Nݘnu͌ |C}x|F1Rd- w]b0e yD(ldfOvƭ:m%mRs8YQ "bv GIeyzKB0C`A ر sɷ.;]ΫLprD5G7/}fg+|BO[T /~B B 8V)p'wЪKI{30W8C ՙt̡ΠT >`8_=WF:ZHh!Vt G`e(]WCY(Y,Q[Wt5IAbR6 t+?oTD͐yoe_"| HTluTףleMWjwFʬ/pG{R\^n ~R[ |I=9Ӈ7 ơ(pã[~xTl-p`O@MUIqӠsȤZ$G:Z~rǍ!С;B#Rbֵ -cL$@mQ ::gz#@r}Ͷ T/gdQgnpkN##@ +)H3-OW (hI͈C嶃[µFtܰ  v{BwN^ĩ2a=K6!9Rۊ0#^)C81qQ0ufKPidyS3O?%<z396g^={j/h>26.#(/I|:S=4gc9B\ƛޕePD$d_I{!$Wf(GXbMpwY" SB^PķO@{и`c$U\)IO#jO,%.ʈsAvSE亗d&iD#`L HaBTX{'D0JzҮ E崔nSrmzy F='K5ժhN+4p9J{ ~ڋbTCb!]HuOvѯu1ƃ|7ü["aڲ?Ԥ-y(?id`IZUm4ِgj$42BЧ{̀/vWH5QzJ b&!e{STmTFzuP&vu%OꖦhGPZ} nZ ERq4%} dl;-7^P00#,. BG|L~R58 6kɷCa6F޼'RH^&w9jR(#O_:+m9u̒8sVl!#*2`&pA;,(6P׎?xȧe*|͡TbT9 75 9FH4ք}ai(D)yqdZ"U7kލ@KVv^pu GOָ53I0N;kǡ-x`nfr 1O&s?;ٛB$ş*UB>JFZz1^Ma[}6ESoJ8.ʒO4DޛWVС@b,$BoJ5HaŮvqܮلʐQ $vSTOC7D% *yh_i ~ؐRww JG?RɉuXd![ k|'㍂\iNԽ#ۙQ\{ | 'm&v5٪Zvڥ3]0oL55~ec mC;jC&EeÑj|;p$, s%ȔLaT_]O.;J4 bRvy?BqkI"4=1^W7"τ~5;UPǫ|dK=d}}HK:##bIu[fB"%Y"f|' 3)T`n5wP!\8^(+i*2 tA9|BL'2>=C2y~OO{AMQу?/)FAU[SȕRV;JӣS>vZQbs&aKjQ0,|VaMD?aO15[ qJUw`FL&S ^I9#WOMEa`i⁧z1u*SNJB3ɩ:,ǤۑM7fws,>yIޜԧ:QZ4K݊h:f,:tv)/n RQw !1h[A>X8vp8q`\\of[4?BN" tg 淹K|fI$(8h ?4+ZAO溁JzJ`L ac$k rWO2~)"p}ȼb=ZHpq̣߬iΖ?zaW+?PX>=/c<\.6\P`A_R%Ը~Zmi3̥ko;H=}rRPHrʅJ{R+^ ;.kڃ9;yd]L;.c_ch d֚\O6\kФD^RY9p.^ڇ+ 3G/k:s FK<672=ZS?ZB fޤԣS_xKxG\iE-Vrc!g\$? :]R^\]"ڷխ#y)BsX6e%1?ZՖrmRoh'Jt>$F"ߘ7vkp bh>v ArjWWS'%*U9#4 -B|TG.MLE60 ˮtzaxV`n&nw!n^k]br?te `Q%G'b|¬P3Dns0Qڟ_׀eJ o,$nt[ 0vUCOʚȡușgl g |^O xWu `]o3|i\A~iAX= bB iLU554<-Wں @| xhFt:|0XiCD*e: 3\ԑZ@p-+ՒLkoUrK2|1͈(S ?\yg%uӻ00mnƤ8$*Ʉ^ka&_1uSw<Wr$ \fyu7_oaQe'{&@ a4hD]gZњe(kl%dE#N% d"Z/ZhA6Vˆ1ʏ97Z3"O[,7O\FzAKN1fWo)3'%qEJTVg]bjh^$%̀@VUDm슻eEUb<-Fˮy3ێ^QhK-6*6`=r!#E oQi| x#|T[%oN#g-G 1t䔶2>e1lpFa9қ-G#}Pq 0g3Uw7CPAo؈kyWU'Dց!"{x1-0bwVC^_P\棲SOR{:C!U@qh܍iy%|o~5SR%KZ=:UEOqbOu+Ln } A~d œm ƱWbJ֨?ۣa@ T}Tfs۸ ހ,ΖLb"GRXIF^jHA3Eܞ%édh&{pqBxodtx$MuAAx2i6-wT$ .Ǝ(֚"Fh2V0bK\7n?byXa{K:=%񱄃bgIԕRV48z8w$l:*aMvz<G?یZR0;p;̌ia%pC!XƅY 4L\YDt=1❗F|"cO#EƤuRt+t AEIwXzhPRgu+f{OahaD64Nb}FxWZyڼZ,\K@?F&lzq>Tʡ!~WvLwZi-FCzhFõEU+r椯 8Vfa&OXlݫ_iN RW8Th7QܷOďO~ B56h:uNc⺫XTTb,ujA+`frm&n| Dhҩxm 8ruԎgd^>X`orؼ0,29eD&{sW>+Zkyi@^m8Ex^c0,H=UBl!E߶4PZGoaU7` Wmx4{':-ϱ͟ !#EUmE˧B?,-Km5fW\Ri0b;IMx \M'Ŀ8AReN7ZOh*<@3m ġ)},^eß5Q*WcX-`th;X21\K3tpkv`M| Ig5ahsR`kfV9lmp7e}gnF#&K] c?o'DS jHT)+RUWUSP(֍!&ML 4p)A!l\#~ 83/ HJ5 R|Im#R O3?֊a*bAN!klzVBYX'tj6Ab+`06B( s>?^Xy 9INdp i^cbw"=$ PyvqK{w;$^]0їguOiGA2u<n"24 ſFL(0$ulQy[2*UGC!gj`ܳB~.R ~MJ P㷍@5]^. 4x$EoyPjX4N- K#LuSXu!\k,aK};rXn!,>n|mē5DLﵳs qv|;_˅cݭG7q6cq3? Rؑ%̩&/8Kֆt2Y(chC?hhV( 3IJP2nI!?{;D}* ~FVXiθ,[@pb)qBFNN8MdK!cՌ8%'z`X}72Dvd쫕S*q0F3כ!>8/~OE]Vep$ ǣtgd}Pq")mvb 9; ıSwr,1+D"LlCT0>)̈XtXlyXieʋ.Z9e cG3һQM/'IzRMĒRĆQA + jYk>~nqިWKC'06eXo W<1 efe'CNo& *6We8'䚥@>:ʄHГB2$^RpPwg'yoH)6>tc%z[V'`8gԔ 0#тZ>"XI 5XK3@I("]MH+ڙc K5͕Fꆅ;ҹzu{&oN4؃ֆ5t91{v#\ ԚBm;&^(/t](C{d`MA,Ǜg ]NjPO_v)}?F4٪Q^ ]̬s^oxVK~:M41gԡon?|1_ڦhT\4dO@Wb?.;3Dβ # kz4Be-U; Ax@bH!{i4\T@E. uFM&"_>p9/3ψ޽#2Y颖`rAמSsGm;I9rz;$η˔{8sJCZ|msuo)RbsqX3( ۨ. 1M,>\g j:)r vHspU;I2R6F@;C?Tv߇D끪$in u߉I5ޝnQw+m/w,xK6sczD{v Rz=qI ^,MY\O"Cܰn`[bIg50'fs%SgكI43CHE2[LHPH4X[4\o$G2"=zFԬ2贷]">)ȓCyF[ "Υŷ<$WgQᅠZV,ĕAv p:qTkAgAjzǭYOTwZ '(sJ)l6RE2gBOo.I#UR!;Y իKbl+2_.O,#p7Ya5@ /`:%GPw9k93͈A:%[̥2,@:'CX]Wʣ^~ɻ8KP1U܅AAELՓBp-.2Ջsf~sC_c/"V*e-I@NYU_gA5` 7z7V37Xǰe.CH  .Ik2*NGQBYw&_!USy@n ux̻dJGTFeub} :4hqVnNGӝ=`mT|b~rE/_K$(=Y̏@+ݾz璭n}wiٸm1%bˮ^?5\8WRc8>^GpwB7L{ 4NV&.L|{`(5L`fHZ`S VOr8㡰aڐE ^l;Bu xW$5\*!@&"f $824b=Cev<) =׍ki38\w'DvXoSG8سq6H8I~)-jORvY l8lq>#ߖ jxbb}bo rap=5qn- &8@gI<#j%1 r ꄬ ! B$e]DJP.d-eD~lgQZ9^'I{GK@Ydy܆j޵=6)Cf%eJN[3.gK͑ˌӶS\K B,`dVrO;ϭ3XK}17.HXuz[8=`d9iqa.SR$Ϥ,œ ( =1cazUN>z߮{lmWJbs V9rxO-ʧhǓ ٣r\0ȜZe?{0} y'E" Wl@rtU=t$O=V_P'u:/:1v%̝76pdD;^K o#b68L^`և]fqK c$yLƕ%oGHRc5i[:-Ii uI`(:2G?rBkHgF+i-i#fpXԠ,VDGlTۅdO$(nD=EiRk[&^y3+|!RlXd'D+}`dgI&Q7`ARࢗA]-5*G'N"$z+a<WÓoʰi& Uu& kc}cZz9[`D/)ed=jAlu1uבQ_􏇇EqBLf1&gKEU0`BÆ-7LGCK?L@ó! ["rW@7=`ctY~oD"jU4牥QO1}"VG?Fr/-E9Y:<9~_m6?eЙaIlChk 긍X=lŢ˚S%CPh5dc_ Mb`74" v.-E[XxWJױm_²0C{ r֚m{Vr;(<_0dz<5mL qr!"g M}D} /1D{;Y l7r,rg-<շv 9K|i8FٹLgNIhLyl(B%.`3: ~ǗJ'R@H"6N2dc 2> <1azQ޲Ҥ4y+}xyG_(_ՀQhwKw\$2DmE 7!GAQ+aװ\:/?lMo8)7;y2|EAbٻ9&~k^7_y)?!hV6H<.t\KԮDyrλ݊_;IpA?eK+kEٺlECm~ Yc!$ %8Co}7eg9S?_pR$brbˊ82:F>WB=50Uu7vZL6 6ߑuIT oQVp5lR* T,0û-a/ػN`vI;J-Q/ݫ iwЭ+mlC?>H}C2!^(. 0^)o/H=8NJgT+P}`(P1G `-^׳&חp7+Yc\TO+Gx !J(r#(e[`A wΈ훔3%nKC'sZ3*jC*lR8Z{= XNOsژTCĐ{Rhm3&-wURfdF䠧F[ϋ:,}4Lt(yx6h}hi:e41}@_!6]vl?F4 1Xڒ*a2$H6;33~ wx4C5X' ULݐ.j#ϱs9-uR]o]/TaXG6#qg?.c[nԽj"ʉ֜ӕ&>M{vi .̃29r`Ԉ|@_Ã~z)eNs)o7ZO}}ϸkpÅ1\}tGXZŭDVD¬':MI'RYfEʈɄܖmlPH/Y]R݊xUGSRmMS#TIwd'`w U39y@T@,ݲ-5H49Y7U#=!D2:J ԫɨD6F oxv>= 7;.kaChlt@dxŬ%4c  3dkPسUԹmq_)Wbyd53y/(qoN-] +ʘµ2>ݨ`1nGRBa,tԖLyb(ӣI_P`51.JGgFD?]Hԍt]`8/aLt?hgqzdTJrX8[ټQ[ {knJEst!V)*ˮ-4BMND*uZhr6Sq+;;TozgXU3x[fTDpKޢY((W9W8EDҞ;.>eY8LI& gjDZ>p+8 {HK{SŘ~ک#}J^E"{֩@mRϦO+ O'`_2=B/8z5zÁ?w4|͐Gg 1u(;&[[~O-]^Q|/@G OaM˙S^&(h  j2R^$"Qĭ9#"Z"5SOΫtJ]QP +Au}W+JsqPSH _]Ly` c XzQphJ?47;~g9vZjVA4GծT`'cv/^*Oro d`~ŖDciiY"]A山D|B[fzŠV?jh"gys[cCv`.{E(.e6E|(UNƙ&nY'}BX \`ʡV|?vYԡ3ى=#@+V7mb涳"bp5IqV){sJJ<|(T>w5ayW4 hZ%6~";zBs25C1ݿH#1鲕 3cXc /GM/vcِs=>JX/|&^ 0EFq%!)PöRHTۀ=M%;psT)_^)^[^ O9#*8B:"-0UX/!xYo ƀaZ[8w) ћw/Op0sNg 8؛²%'a7pa8R'tbcmu?zkR ݟsd^(H2 IF]eT<"2i; ӲDw3C}b Fp5 |jn3u(^úyLҝXc2IXҵ3 $4~1[A[%JKnb(y_i{{)T26'һ"\{eY'G8{Y"W@VC޲",q^vqZvu_9Z`VH\Ul`rz-Qb[~b}7q_6}=o <`MvWu"BLtMY%xWE|YzR{y^Z%Q+6ACtB8))%? 4wZ-IH@l\^":ʔs)ZuRɡ``SfDAʎ75𗘫*zm;%;5 {]&DqV)ӊ!a(FʅHTvCt(Wp@5; Yr'?™Ѓ428 V(R[~93`{ [hVyR{L6/jb+|L颅7Yob DCо8b/V'%՚JMX4 +r[;VinޡQ? =3FdZ=Tx#`_ .Xxpll^zoHh06 ґ%4v僨+]5!XT y(Bxv(ڃ;D 6@CmB?7*'8sO6iǜs7[gѨ]r պ,g *{$kuuX]vLtok{@HM 7z FLpx0•>c*Q^biE ;+B!9IdQ4q 6utOǸyi=DfUfӓ9,ZLƸJJo>ZC5DVZ~Rn767SK>(IFXLՖEcqDlTA9ҫʩz&B4xr3r <)":,FtE=OI2V:ЁP VC?GP|7D:Ul o`XG_ T}h  7l2f4/q(?ZNIxB*-H/d0swl@ԡЧ+4Digm`3{wJ=9%.K'v.GV 9ڳV}"`iXz\?DRM1;̤6a QTrkL#6R}΍J, e2{`ӰBz;uy@ I´%E5p*DbMYhV3`Sq'Dr31~+nY8TOb-" FFM /4>wzzzܸ LAJe]FQlbNWL8a up# |w!2U Otq@G٥%6 "ÜZ0Nͽz;r4d8m93j]3&ڃ|,PlJur-GjXa!p+xm}A }t|Q4qG+va/k<,v @~ {VX-ϟ H%opSj0LTNPHQsO)=$O6h8KMwpLHE R ,T$qmoM#]2s Ȑ79N6 }ZC)LFqbF`.TҰ\ ݬWˣ`5 >OZtu^a*^`JDyjtZz:lXe-`#\9cS:M#7kG/Z"%/zo1j-cQE~GOU=5lEKpjuRK1N@t?Ρx/G I>(7% G)i^uXլU,z%= (7 m=&!@p'W@P :lL9!\V*G=;A 0O)rG7ɵ`zOX ^L2>\+֞U­(go~҉MCǼ}!̔7LB ɯNnK\Xm&ų f_Zؐ$Z[ĵf1J9.Mu]C:!2;>NgƵ6!NmqtF88TnmpEj BK͈>%׋4F*cq& m2G~-G>&W$[BA6m؝;  fvTݲ|Ehzjh^)C@0mZ\4!y=a|Sn݃t;8cN!RptÃ̘&f U8Os>-z !/j6ƄpAϾf2J  6?tJ03i$ZD冄5_?ywɺj  jnW:R0OX,FEYTh :x &3IMyz tĠ =?hC3zp5lEM0S ckt"UX/]1&ai3vҼA?M#*إ-|gE筛3<164M3hx!/> B `mC<8BVK\^ ݸmTQsz7WJUk}lnl6Fg<Q=d&Xi(lͷ`Qfe:WvX\ENB 0˾W5%$*KF^ 9;Ւ)uEaUfҫtX.lzsy|mJ&VeW)o'*ST?ּ6:=]a o`[,8q_]{]ZEPt7^mBW{$a~yph_{W3l b[=OHgS< t0'4N?ހ*`!!6߼ Il6)B0eð"NTd)l8q-/K<\ n/RMbrX5 1OOrI5?sLdlWkÜfuT!/3teR (yNF89cY)q_C6'}Uk .c,  c!W4ei+̋>ZT_NDaA C ɥ4\gJI :-l<: q {TPu'cԏ8BjkEf rni~)8_@Z̻RcjG@زN)Sr\کL}TfxkL=ȁg{|aڊ.cEfcSj`HUUwݳ@m>!I9حh<7se "GrON_ {:JYXhsʌʩdTQM͌g}NXOkˢ@S烲Pp1hNKѭxaR+fC`JWF[{7y% 'qϮHB d}Qboȫ] ]%yyI1 .1ƫT, ?j$}:$:mmY][.+kE52aV2Hh2 XkaB9|=Oo edB͛hh*4F9 E#0VDS9[bc̺޾"HPQ|۬]D| ցifircZ ː%gXog"@%q&CQ/p ^wH0%&6֎!Ū*?X32cKno1/W, ,3PxRV fk$k-@t;oS/ڏx;"=a&H#2QNeXrHJ S$!?yO%/dP%Gp@ (UŕDJU",qt+@k+ ÛgC=\*;mrſ $t,r,0j˕ `W63nV>Ђ6i~C"LQhC8nqZ$a]1 Wڬ!24zk8Q/9%WgOqi crvKNN.gpak29%_ ]CQ{g{O5Z"Kh@,{*~0㲜)vz,a)6~ЩՅY@#Ծ7.A͍@)g2qiͷ闼Y]Is&sDwHrElRWt(8/`UJ*f6 \[wkYE(Pd£ꃹ20c-ΘA/|ǒ$-%4 nvu. TޥqScXN,\i̎\Kl}>tL}Q!6@ r?B<ߢea뿯c٪ Ƀ @N,E&{S ΨM)lLN- +8,E5՗7[䘘 tsfi("9RS8F`V?PeE/1S?=hȝܩ~+'TbW=(hw+_hbBf^EòwNMMRp:k':z4IĐrvBK۷p@$h" ayw|Wx!*ֻTc9t DƏh%Ol F*x^b%7du:9n">qo-{K}E<@_m<7X6/: A+-: qB;ԫm.ባ#$K0} x\b:/I Wh\j l0<5Xt'6C=w>nA w.vA}~ui%ZuOjnpuu@Z#-G5ZEqT+8nv~ax|$ur`{/L&bti㌨+Q6uζ!U#`fq|TPc0Ri /)v]Z6,Cʟu$Z/բ^겛nsYPmd5-t#Dvl|bLpGby6~R;R i\Ɓu˭ 䰷6hnF!{IFA*fAnz3|ʯ9LZFhH"gGFu ?ӿȒ$JXI-^)4VT[W ;jfWR̽v# ep},]$ȴnjvqhi†R*YƲD*|t9WcN#$[ /uVy:VuM8,,TtmQTob'{]n;z_3Z!;џz?"q< RI/\, XiG)B'CفojC"@O'huZ!17SbE?UM!ϏzN&3Kr!I`WA¦.[ZEz9D+ ;b:tj&Wx>W؎kQH[7ZA'֡"|^r-6T0eJv @^'cԌk7Uno|@@ JFUaUҿ7sn/!6c@U2eƕR]N+u5&3eӄHBwO6K.EP|ݐ1j716nƍlt4R@ٱ9r4E ֵ} BC3['"܁e!k)F\%"NwL,ڡ=~}Hun^|:>N:n0g:mM/;z&G` ;WeWc8]/BMj%ȌH(g]ZaXq/,R.9Зy6U^; wUGk00xy`Crs|bL9} N'tLh=XkTY^0ReI*A?⅜owt|oŠ4|߯NXg>IWd%N75ZMzB0xfG&Xn&`WEbF@^8u/>mh/O՗/\NT-~06{;~(kxMݥ{dJZ{f- (,ߺȉmRVe-I  5|~]!6f^nΡ.Lk u4Z l ʸ eǯ| CۢlD=(P"/CT?w Ʌn%/)!C!e2,~-),caq i3rB I|PRӤ|4x=n 51~h"!Qn 35dIXFv&#/kಷPViHIQlʯ[Pק:efu6`dr<3BWi|܇8zdTo\~>jn(و0qLG6 tPNܭwܷ2 (״Hvp !F墳6@A$h ۶fN$/i @'߱ v)E{~43ٚ_~> Rs1=RӎZa4iMuXF7s .;q} 5nzߠ~gs[͎ כDg.'fШRfvUP.& J$הjnK2V#fΟ/DQx,mŇ~Zc$j<#k.ކbP)ӎ$,7Şq{bc57p 92J!`>'BiInjA$`Q[ !U94$5ȱ+rd:רDd LP8p?(8f8`e;vs{%w{3Bb~g) OqOQ9V"PaȥzvNŗȇ;W 8efb\Hms9@6 2*"Z{JʾΞE~&ЂΈ "Z=x ݃I@c=Rʴ':D'ǃ qugWLٜ:&SIn !rݨFfv t=Vk5u^Ef#6n,Wy#j񿑸|<Ψhc uր}P|kKodͯɽam{!£"XU ZMd\,ozr=#eT!Mo ǫm>MP1VQ6<9=),(tמ?.5puq q]B߰VJ?y'ITX_S&^3x؎/ \M|`"cug|ꮞC`$-'؈F~=ЕDk1+h+37W)r]BJ=}rB:hJJYgةɎQ\ڱ1B\/ѧ h6Y,?`ivCuWqڬ>@EdDP)/lf J!2i9_<àyӌ~ ݗlr5׭AyA=~@X%^ZJ7g= )!Sww^5v}Lg|R.K(FH:c$n5Z61.T[h囪K;vf r5@YuxKWI90y< P}*) O!=Fh>QB$b+hNbE@oY٪9I*+ Tn_ޠrm@GeV4{Hd*e \:ܐtZK`U Z;ŎH QeY<3|ƽ-ª.tM_a31u|>dՐuSl6Oz(r}CYْ_Uſw3Hj忔^*owZBŗvS?+a*øM*ve'%Xt٬]'gmz?Mh3^ƍJkLk㝤VbWZdM^H>վl5)m#Q+Hi9-ř'͞E@gkWU.aj٢8+4nab1},."BbVUj~!m~KfEp["i۽uqT y9<\]ːy؏5. oለ:]2iFm{F kX? f4A/6sD. ĕlҲ1S7*ܪݶ楕`F_QNRE6J4PdB4&8&x#J-6<<F|91< 6o,mIhZ2;WZOGOd8vq4zF,Q2rZc{@S*~( ?ܭ2F :d ~YZX~<Ά1?r? d&pB -W2VC"o-*LI_fxjʳml,4@i{n9z8dݔ2 ;`GWaHd-/p@3٫aɃ%"E>*3"S=rhxv؉Gk5]* KA,D+# KrnE'd,U@r/^&jLl ]V Wo(YSB>m*҉5[*BKg$"hA$t5oɚ*RWg VkHp2HsK) ߘ 85vP lf6.gЃT+m v틠9ˆbh .F{[E&ٰW("`gRgx)S&>W](')+nQ^5|bX!q&\r`%He/nfd}/\TLV!\1CnHAϋjpL|ipCf:f 4#1;=dKUebto47pu8ޞ?͆ |MѪuOIa[ǩKA[P3Ά=-eHZ,p =Vvb-P"19LBŊ7Q?K T<]EQ/.QA-B.״h&ʚ]+N;3M(.PZTe =2g[e@Nf8:׿Z9O.ST%f(S8<64Xu7uYsdAk k\:8l>}џώWuwlS*gF@}mf U"xT5W Snحb&0X>M~GG_2JH=촤3 d+p+sX)n%)I=623Εy'ݿ)r+.5þ&}3bUF 4&4 .sLxIg c1(my%#i wfɒҠ ;#L06,Nx(^R/)I΁|إ[Ÿu{C q4 ׶vh6z-jxѩYXDl(^[|G2`Sw}C_Kss67Ebk)oƏ_0j~q;qCє0?B~p-t|;bdyZ *\l;cDpN_HH6̓φ? [)_UܫB %qq"P=?:HJ XQbJP}"sgs*-e! F|śjuP{$ATʴ2߰Xl"Hq6C|+okvҾ48.<݄餽Kh۽OȒՊ#&\nQ JOF!_]h**5#i)Ǫt|)wG'ܴnk'ٹP%M׳^fI u 5]mSr*~6])J> !H&#Aqeטl 4X,\THG KzŨHw\BHMǽp*,o"XDKpA;3cxW0hwSSGAS%=3]M^U׿_$2]h^7+j1N5)!w6g.kF_366nsXj'z`ʯS|lXG$UCi1~}gbrʉ"Na3O-mV?(/"]i8hw] zt'H4I!^8Nv@4,R{L7ch2bjPw[Ɲ}d@J"`x}=>B.Pa^e$u"Y~=zцaۙ>[U8j hfZ3BisxV8/]R!fp2erN6UVxxNN{W`%;RЮoO}N%5)8`^U% ײvL:|F4KzgZ%;"Ewi>ʁ@l#৭6fĹZNj @~=&MW#e/pǞ 4M :uY42^1Ų6' m MA!:gKעV&G7h?]r +i%,JAg|^Mͽ4,]$[#ci6W @'ȝ#pK ?,cnSD.,Sh{ItEΠq|Q(A u4s@4]dZi>n-59AU;oSI66ƮL?Wn^5|B xoG6v Pf!0Ocqm), h!,yfM^'|JY3&Wo؋ PI+QwnUԫM$v6n%6P!&Sg;)GYŽeF9T)7QӠǦebCJ!5 oyx߽?ϒH8A5@UGQLt\`/?; T T̔U#>8~͆OKFp+IeQ fPYu՜;]Ba eMҚ֧,3ւl"xmD}FsFoW+Qj:>`GL@e KcډRmNᤠUzTwžBsE%wO%wwŇC~^5.Ɛn.L}Mf R}(Պ7vf]C]<)20[Mo5FC(wOOndV6rN_m#GqaZl?E!N/˅#? }Fh$]7Ř] Vx)9fN>\^9CMC;OO㿰 &0܁#Lb8+YSԕ|Ĥ B4\t6QBPc:>ӈN:\^䆊 ၪ2)Eq0w& xƴYTVxq qmr!eW7+0U"[e?V.xwq7:ptD_za1Z='ڞp-d$F>+MDI(aWѭN##?Jpvl"N☜a?Q&=T+,OgƖ08~lh$S{ ըj^:ܗM*tWO;~VasxVѯ+avh "c ŠlZy>;(K9HG=pKx>[ky&n-;32VHZwGVWRI6GѽQ~EYEÆKf0\+@ .:x(`}*/=Q;ħ nLueuDʍϠq[+P:aQ]A͟:Lȩe񎧠}A-h엘e,?x)/H8 ژZepfY.5$ Yh1M^:o\['>6ie7R{Č[+]p{WDs_d|'{%Rׅ?XDoЯ߃M:^i8*2/eUNHMu dr&#vqY<=E$18Y+KR A9\"E<錠g<6Fض]d!j5|ؐr|1~`AHwi[& &&W:#{[[a=CB2ɢyqwPB `nWfSղCx9TpbWҁ.5g| 2F`0 ߬TU6t@0<2ZNtoF2#PC]i5 -L kq3ko y<o9q: i(1؂O~v2ғ}>3#?x,l&gp.chHGoMh 114ĈfG]+&ʟB7Szlyj)@ѫK-$)fd9O m.Rڐ{:ધy`XS/(Ͻ7GLBT*`ֈw%iZ!Y}X{*m3\}ՂCsF+IЗ/$]YgL~;g‡|hց~+B_)GO]UMU9 !9a5hZJElbgt,ñJIPU*^p|m.x]Dΐ>My1T"4ʽɧ9~ >2%=گ/8Bd.w6 Y&P0sP*q(ء4ki!TɁ$ T*|5*l@xk)v198*57PV&#n yiWR~mf#@s*6̫N,k|}'Վ{hzX6;-왛AQ:x?BꢽZqvyWnhyi{S45-'&$ ?s4&M!ՃB;ežEJ#L8 -~LlPg[$O#.{՚I aѧI4}_,|{Q=ls=HG;̷1VҾN8 E0qW8 SG&)8v'ZF0u#SR;!EWNCw|4#. / bR(".{#^=.۴A)NmYG=Lljï2TO<2x,%ˆNw Z}Mm\0%@;m>jGst<5J|!U~2pXD' U|_E[C}Ez΃η^+8bi|t3Z^52A|J#+ЎdǎwUI3WOIN_-n&Q> ;E52NȘT](-|=D)^]묐# F>~v3f#|D /l(!B2/(b5ݣA:t V66~Ex,AcA%:p@ɔSZ+M)<%yp0İ^$s95Bة%OA% 6<86͔,$nar:(7tWaSv5w8KɆ`&/eUɵw"7c{rD  .#qqQl{B Bo3+J埃ͷ5br_ɦ(T!Ԕky|4. RCQKj~vwyGW'jd0 &z۟SNFdSTN-c2X$B7yY /y촣WXcȁ.E={iJ'a|m1wQ!KQ⤔04=7cu(5UЂ_(~v{jӾ;!B1Dkg@c Z:-(J, |@tT_$49/bV)4هY}0s]??*7fcN*O!X|Y.]B^!YIȜ 8|HbkcvV_h04+[;KlO1(3;/+*{0/_7}h~m_P4#K$>lhWو"b]]VҀHm7nqO]p{Oۂ"{k`*y!ɧAG}h\6Ħ GMǰenV\QU?mV{s`摁0i,"XI^NUhIV 5%oxvgZ'Ϊ ݜTغoaj#4Kh[/i.P]SP At[K,rڛR V ╣iUCi"c6Q+GaO^hv~\tL'H׉Iz ?BK 1GDhvO ͎Km ݼbwl2CW luA#e^+awyΨ҉kjM+ X>v91Ow݌GgתOΓh)Y" ['|mICD6;AQ[r~^:'0|p2,ӧkL4 .4J(h_킗n4K6%ȿ|QFՠ+?Sؖ`Q^M6N"Sn(& m QI6$ve)lvEw1zRuzSvNzAHSzH 8i/*0]SH}%*2={#3^뙿Y%ߠw9i"[ ̭;X`r{M?몥|YϹ$*QT/EVՄ0y&r}eU纀.S3*DZWQD˾`{g;V&RtMI nb8{|)Z{/Ӿ`)Eb0RsšǢ7+~C1^`$?S8订uR  [A}Y(uǪ3HOm6H]pӎRͰy }8Kb([$9=޽I)e@_/|On3 tc0\6 @fE\ͧ5Lё"j8P/6?J7(.Pق;@~)Qc=驗^[NӖNmKl\]>ߗ Qv|c@IN}T[Zs:|.vDdCgi-P*wqx WMD-#yCOOٽIoT~Jv׫,~C)7$F劰b~.` 4#;4h޴H6V9U9;j|# ։7KzB M&LaXo=W+t6hB y*bXg"dQ~ɦ?;ϊcw  !Ig6}77 wiᏯ{"lP0=MU>ƠI9e/J.a* +>65 h|PddS K K+.qƿ^p9 ,c/ŵg͕w 6G"<)[cR|5+:~9ݖlDuiyԹ]Z YW3-,*jc׼/&Y3]3HeFSʚ1LP( MpI=27̮h2vٛ{%?2J*բg+5]vT)|Ďb[t띉> mMPW;O^Y(7*+fWeճq*J32 YlgKmve}ȺRd2l<-EˉمCS&eWs~a[?mR+xШaqnMn[#le!~";R/>4k<y^mm0 KL܆#-ܗnO/i_ NS@xapTO§%hCq!-r΅(zIJZqᅞmk9@ţ^eP=  ks|'m~52;t6SDtuEEYhE¿=xvKwlz([U;^`y{Fg_& 8y]H}4!@Uygaly 8t&xt2(ݷf,*d-52:4Mvq$ ({2e!"ْ,sd0-qlLV@Xr Ѹ0 U#]PlPw0oܙcJ}_x}YR@Ȓ[@" vk/C(ۿ6ڰ ,7zyuJ@" l3?A4mANavV0iRML&}cXqj9|*FuJ?$^NPkz%_?8ė=YJNuj4ɿ#Y<+1@x'))2q6,DZ'N`yozOTRqI}czq7 b GEa!{&8Q*,fGlP :Z7+p31|:Z#,*8۽PD HQ!"./bD>R#Ͼ &x='1ӔQ)Bo-:PH_`gT_~Ƌ-8 K \Q\JId5:́:W^+{xkiKSZYq@%;\VѤ]W2Skm:? 톝]E@+e<6&/-AQPrhѰNR,~̇?!|XHAm]aM~dvd|m@vrfc/TO՜5jČV6NKEf2\=m’;R3J;"~ Ќ;4w[8/ uͥawvY[%Xd 4l Hn3C)ʣހE<>$DªwZV8קt-BQ'9\BiT3d҄'G n(ɑ/Han# 'CܒI! p] <$dE ͿW6@#<53!1<{}Xb+ `sUG-)[(x7PLլdR˄:_貚PVwJ)م_~O3v2NR4@.`R!ӄU4%txwP:&`/@przTf 1ne5i`ӺTqL{'W5<lJ_9u%B gD5\-z.#{, `tkE-UhJ6(w'Ɵ1l.'?'"a/_b+j ?,= v4b0lRFo\P.BݽT \ qU̓To1udZI%9Ohc5GFSwrN E\cR@AEv6%le b9-~5E?['|f/C k-_Qe$5Wo[J.R? [a9<`,욖41NCkXfҏ<Ö)4h^IIn`?{L؎/>Э@6.`(MFW ÚB1Ohm{F& d.߂xnaXkpo:@!pzP]6ZҔ_3+$Hm`]%Y\wTΩ\-;C@zBxp~Π SC/U$/>Sp.ں[vc@Q>csXSEongpT?h,5(&gLSn!170V`tOzΟ=s&Õb=T9%|ȋ䃇,(fQfYιfK{C!za Pr >Ɇ1>TWALҎS3bޒ ʿkX>jSQkSN߷u+@Z_apmzۙHZc40T_+^jRdԝ)nUEX* }#k-o)vMGd>%%<^3V aYej)FDX{ @CMEP騊~{b.CRVGKyR#."yST+ϓaukܦog} G`{ݢR}p+zxRJK[δMX-k|Ǫ#6Q!k I/e2wS9(fW_Ir]N%?6^!n X a.)QUb.u|@;UC"eAB^ SȊeP @T,il9i[m:έ-<(v9Z}-;!F0sԶ26>VR/QVbuC=Äߧ.}9iSqLɕ}Hc& (ݨn(w|r\4^"8'%Vq;cjӵcW[@GL5 l ?YYtt3dkb./p`TvDLb5i7V;l;@CW>m\GW1R1r߷B&%uzɭ@QMK&]ewb[!hȚ.ό7E{+88.˚Թ7xaqADmq'R a6%Xo|\&uRk_2%O;T- D<+ 㰊:.>xxn؁r,d㣏Osw&R'Tux;?g+'}n3(@?¦/s~qícP ј @UaV49Ek?Y "|Щ#Bxj.T/٪-Gg{l |MTD%1ݯUʧjZ,q X4^g+y*BG!m+GcԞ0A`^e#F@OP=eWt/tm Ǡt|^GN X9{NDr7s`tKu4"Xv_<&S b 1̵]E$#}pg>j=̼3yR EYoI"V6K_%G_OcFb(bW4L_I呜kZy I#Sw o'djM󡫬dVy]vC?;j+ *?A.Ygm裀ZbKęnYYՍ/Ö&˓7e*]Jȿ&$#]DfHuE!;hJ} Ά3[ŨE5:7z6 ɀ)|  ],g`j7vD¿P@\'B& !J+{J`.b4n`sTzܥU9NSл(Ǽ2 bE cmSN縮~ FR]IB֬E9/% #/7( m6|rB(]B<}n"D<]/hLv մprw:c{n:+w$lpvnI#MC@S*$s^ƺ~Q|N "lu(S׬ zgmܧ6jkO~r=Frn5! :FĆ<$7At>L^33s<1rȯ@?iv E,3DĒ"v`ĕw8\y0t5'.E\>lG"FOA1qԳeK}xv E)fQq|Gf,=#ȶӎǀm={/ 4YtW/o( |M>55]~hv?SiK)Kۈ;H0D`T7{)тSk+&R0p*о>J>ɑ ԓIm8E-0>!HDdS}k|5ϵ *> n{O5:3{|[8nwoJt&kKRh'6ǻ7<[hTZRr_@]c*ZGQV@3j(LN1%='fF@DO2mw9#tXM o."%|!/ݒHtQ:(I^jƯ!IVXMBZɉaΧ3&Q-UxR.1t2:}144vF%e}׾=Rj |6,W5i#me톿aj®J'?\2e-cp ;g6E˝>C׼O|}秅 ~ag $o"<5C&I/^Ct34Ug`{ݐF@eV1 2)liѵ mKGLs)K1д{Ji{##DuvwC%Š\`V+id٠m;=wǩVEW6?6jb[cI{:ꄣy Җ?ԷM)x2?oec۫7ʯZ;o `ehH~(N~%CwWEBݥ} mzak!]%K7p!f*Q61@X!։KARzESi?uq}ͯ)Oul.we;˜aF.ހoPU%VT?t$i|a–Zv]{Nȫ=O方뗜I@}<P4JZI3Q/g?Yx21@(6:OɭHތЇGnHFOEj߃Y7ީ]g P *2SQQaIfMɩBa SG4*JW^O֘I2\~QNT. J~8dB΅2O(/;Uf1xpna:]٨q:8&,ܑfUP{I_ U쫠ҎKjj`EvKzPSſf=V$HDfJM&<@V^/Cs{'3 |4,}w ϳ0OPqCxyQU*8I/<ͥ7YZnrjU/qB2t_@,uc]uruyINOE9rbRm]@eYb#ʧV.զ.7֕O7DvcXq8eooS kgӰɦ^eRªfKbf費+n/uIԶ\L$ck};)uWg̕H-CM͎YDe\]6dt̔RZWWV"jKU'0Dx5bLJgJ.5t>V#ID#|@kԽ0v?y