<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.yobi.be/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Newacct</id>
	<title>YobiWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.yobi.be/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Newacct"/>
	<link rel="alternate" type="text/html" href="https://wiki.yobi.be/index.php?title=Special:Contributions/Newacct"/>
	<updated>2026-06-18T22:59:45Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.8</generator>
	<entry>
		<id>https://wiki.yobi.be/index.php?title=SAGE_%26_cryptology&amp;diff=6049</id>
		<title>SAGE &amp; cryptology</title>
		<link rel="alternate" type="text/html" href="https://wiki.yobi.be/index.php?title=SAGE_%26_cryptology&amp;diff=6049"/>
		<updated>2009-12-24T06:59:37Z</updated>

		<summary type="html">&lt;p&gt;Newacct: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[Sage]]&lt;br /&gt;
==Discussions==&lt;br /&gt;
* [http://groups.google.com/group/sage-devel/browse_thread/thread/9557f299b1ec2fab?hl=en this thread in the ML]&lt;br /&gt;
* [http://www.informatik.uni-bremen.de/cgi-bin/cgiwrap/malb/blosxom.pl this blog] and especially [http://www.informatik.uni-bremen.de/cgi-bin/cgiwrap/malb/blosxom.pl/2008/03/21#sage-crypto-todo this post]&lt;br /&gt;
* [http://modular.math.washington.edu/sage/jsage/ JSAGE]&lt;br /&gt;
* [http://modular.math.washington.edu/sage/dev.html notes for developers]&lt;br /&gt;
==Docs==&lt;br /&gt;
* http://www.cryptlib.com/standards-compliance.htm&lt;br /&gt;
* [http://www.sagemath.org/doc/html/prog/index.html Sage Programming Guide]&lt;br /&gt;
* Sage on Google Groups&lt;br /&gt;
** [http://groups.google.com/group/sage-support sage-support]&lt;br /&gt;
** [http://groups.google.com/group/sage-devel sage-devel]&lt;br /&gt;
** [http://groups.google.com/group/sage-edu sage-edu]&lt;br /&gt;
* Python on Google Groups&lt;br /&gt;
** [http://groups.google.com/group/comp.lang.python/topics?hl=en comp.lang.python]&lt;br /&gt;
&lt;br /&gt;
==General Remarks==&lt;br /&gt;
* When using Python code in Sage&lt;br /&gt;
** Sage uses an internal integer representation (&#039;&#039;sage.rings.integer.Integer&#039;&#039;) which differs with the &#039;&#039;int&#039;&#039; representation of Python and which can lead to errors in some circumstances.&amp;lt;br&amp;gt;=&amp;gt; cast &#039;&#039;sage&#039;&#039; integers to &#039;&#039;python&#039;&#039; integers before supplying them to a Python function in Sage&lt;br /&gt;
*** Use int(...) to cast a variable (ex. int(var) )&lt;br /&gt;
*** Use ...r for an immediate Python integer (ex. 384r)&lt;br /&gt;
*** more info [http://groups.google.com/group/sage-support/browse_thread/thread/49f5e14f36083782/f97f6997d5164da here]&lt;br /&gt;
*** one example: &#039;1/2&#039; in Python = 0, of type int (for now, it&#039;s foreseen to make it a real div, cf __future__ module, will be =0.5 of type float) but &#039;1/2&#039; in Sage = 1/2, of type sage.rings.rational.Rational , to solve it, either cast to int() or use operator // instead of / in Python&lt;br /&gt;
&lt;br /&gt;
==TODO==&lt;br /&gt;
* check licensing of different packages and compare them to requirements for sage&lt;br /&gt;
** PyCrypto:&lt;br /&gt;
&amp;lt;blockquote&amp;gt; I&#039;ve filed a bug in PyCrypto&#039;s bug tracker: https://bugs.launchpad.net/pycrypto/+bug/260130&lt;br /&gt;
 The PyCrypto licensing status is a bit of a mess.  It looks like a bunch of reference implementations were simply copied-and-pasted into the source tree, and each has its own licensing statement.&lt;br /&gt;
 I recommend looking at each source file and making a judgment for yourself.&lt;br /&gt;
 I&#039;m slowly working on a new release of PyCrypto (I&#039;ve just taken over from Andrew Kuchling).  In the next release, I&#039;ll try to document things better, and fix the most obvious problems (I&#039;ve already written a replacement for RIPEMD.c).&lt;br /&gt;
 However, some of the software is unattributed.  I assume that most of it was written by A.M. Kuchling, but I can&#039;t be totally sure.  I&#039;ll try to contact Andrew and see if he can clear things up.&lt;br /&gt;
 - Dwayne &lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
* analyse structure of integers and strings and see if the representation from sage is compatible with the one from python&lt;br /&gt;
** Integers:&amp;lt;br&amp;gt;Integers in Sage are a different type than the Python integers. Problems can occur when executing standard python code in Sage. To avoid problems: add &#039;r&#039; after a number to let it be interpreted as a Python integer.&amp;lt;br&amp;gt;More info:&amp;lt;br&amp;gt;Google Groups: [http://groups.google.com/group/sage-support/browse_thread/thread/49f5e14f36083782/f97f6997d5164da0] [http://groups.google.com/group/sage-support/browse_thread/thread/a815fa8c73411da3/6c967fbd741a31c7]&amp;lt;br&amp;gt;Sage Tutorial on differences caused by the Sage preparser: [http://www.sagemath.org/doc/tut/node72.html]&lt;br /&gt;
** Strings:&amp;lt;br&amp;gt;Strings in Sage are the same type as Python strings&lt;br /&gt;
* analyse the format of objects used by different libraries and see if they are compatible&lt;br /&gt;
** Almost all libraries used &amp;quot;binary strings&amp;quot; as input/output with some exceptions:&lt;br /&gt;
*** PyCrypto: RSA signatures are &amp;quot;long&amp;quot;&lt;br /&gt;
*** TLS Lite: RSA signatures are &amp;quot;array of bytes&amp;quot; (defined by the TLS Lite library)&lt;br /&gt;
* write a unified API for the different libraries&lt;br /&gt;
* write wrapper for internal C library&lt;br /&gt;
* check [http://code.google.com/p/keyczar/ keyczar], the new lib of Google, also [http://www.keyczar.org/pydocs/index.html available in Python]&lt;br /&gt;
===TODO for Phil===&lt;br /&gt;
* make some speed tests with [http://psyco.sourceforge.net/ psyco], claiming to run python code up to 5x faster but:&amp;lt;br&amp;gt;Psyco does not support the 64-bit x86 architecture, unless you have a Python compiled in 32-bit compatibility mode. There are no plans to port Psyco to 64-bit architectures. This would be rather involved. Psyco is only being maintained, not further developed. The development efforts of the author are now focused on [http://codespeak.net/pypy/dist/pypy/doc/home.html PyPy], which includes Psyco-like techniques.&lt;br /&gt;
** so to be tried on the desktop and if efficient, for the IT box&lt;br /&gt;
** see also [http://www.sagemath.org/doc/html/prog/node38.html doc of Sage]&lt;br /&gt;
** here is an [http://bitecode.co.uk/2008/07/password-cracking-in-python-with-psyco/ example] and I tried on the long2string() fastest implementation, it works even on so small code:&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
def long2string(i):&lt;br /&gt;
    s=&#039;0&#039;+hex(i)[2:-1]&lt;br /&gt;
    return s[len(s) % 2:].decode(&#039;hex&#039;)&lt;br /&gt;
timeit long2string(123456789012345678901234)&lt;br /&gt;
#100000 loops, best of 3: 2.7 µs per loop&lt;br /&gt;
import psyco&lt;br /&gt;
psyco.full()&lt;br /&gt;
timeit long2string(123456789012345678901234)&lt;br /&gt;
#1000000 loops, best of 3: 1.44 µs per loop&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Try also [http://www.msri.org/about/computing/docs/sage/prog/node7.html Pyrex]&lt;br /&gt;
Setup a subversion server and explain how to use it, for new Python code developments...&lt;br /&gt;
&lt;br /&gt;
==Available==&lt;br /&gt;
===sage.crypto===&lt;br /&gt;
[http://www.sagemath.org/doc/html/ref/node171.html Sage Reference Manual]&amp;lt;br&amp;gt;&lt;br /&gt;
[http://www.sagemath.org/doc/html/const/index.html Constructions in Sage]&amp;lt;br&amp;gt;&lt;br /&gt;
* sage.crypto.all&lt;br /&gt;
* sage.crypto.cipher&lt;br /&gt;
* sage.crypto.classical&lt;br /&gt;
* sage.crypto.classical_cipher&lt;br /&gt;
** hillchipher&lt;br /&gt;
** substitutioncipher&lt;br /&gt;
** transpositioncipher&lt;br /&gt;
** vigenerecipher&lt;br /&gt;
* sage.crypto.cryptosystem&lt;br /&gt;
* sage.crypto.lfsr&lt;br /&gt;
** Module Level Functions&lt;br /&gt;
*** lfsr_autocorrelation(L, p, k)&lt;br /&gt;
*** lfsr_connection_polynomial(s)&lt;br /&gt;
*** lfsr_sequence(key, fill, n)&lt;br /&gt;
** Examples:&lt;br /&gt;
 sage: F = GF(2)&lt;br /&gt;
 sage: o = F(0)&lt;br /&gt;
 sage: l = F(1)&lt;br /&gt;
 sage: key = [l,o,o,l]; fill = [l,l,o,l]; n = 20&lt;br /&gt;
 sage: s = &#039;&#039;&#039;lfsr_sequence&#039;&#039;&#039;(key,fill,n)&lt;br /&gt;
 sage: &#039;&#039;&#039;lfsr_autocorrelation&#039;&#039;&#039;(s,15,7)&lt;br /&gt;
 4/15&lt;br /&gt;
 sage: lfsr_autocorrelation(s,int(15),7)&lt;br /&gt;
 4/15&lt;br /&gt;
&lt;br /&gt;
 sage: F = GF(2)&lt;br /&gt;
 sage: F&lt;br /&gt;
 Finite Field of size 2&lt;br /&gt;
 sage: o = F(0); l = F(1)&lt;br /&gt;
 sage: key = [l,o,o,l]; fill = [l,l,o,l]; n = 20&lt;br /&gt;
 sage: s = &#039;&#039;&#039;lfsr_sequence&#039;&#039;&#039;(key,fill,n); s&lt;br /&gt;
 [1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0]&lt;br /&gt;
 sage: &#039;&#039;&#039;lfsr_connection_polynomial&#039;&#039;&#039;(s)&lt;br /&gt;
 x^4 + x + 1&lt;br /&gt;
 sage: berlekamp_massey(s)&lt;br /&gt;
 x^4 + x^3 + 1&lt;br /&gt;
* sage.crypto.stream_cipher [http://www.sagemath.org/doc/html/ref/module-sage.crypto.stream-cipher.html]&lt;br /&gt;
** Class: LFSRCipher&lt;br /&gt;
** Class: ShrinkingGeneratorCipher&lt;br /&gt;
*** new(input): input = connection polynomial &amp;amp; initial state&lt;br /&gt;
*** decimating_cipher(self)&lt;br /&gt;
 sage: FF = FiniteField(2)&lt;br /&gt;
 sage: P.&amp;lt;x&amp;gt; = PolynomialRing(FF)&lt;br /&gt;
 sage: LFSR = LFSRCryptosystem(FF)&lt;br /&gt;
 sage: IS_1 = [ FF(a) for a in [0,1,0,1,0,0,0] ]&lt;br /&gt;
 sage: e1 = LFSR((x^7 + x + 1,IS_1))&lt;br /&gt;
 sage: IS_2 = [ FF(a) for a in [0,0,1,0,0,0,1,0,1] ]&lt;br /&gt;
 sage: e2 = LFSR((x^9 + x^3 + 1,IS_2))&lt;br /&gt;
 sage: E = ShrinkingGeneratorCryptosystem()&lt;br /&gt;
 sage: e = E((e1,e2))&lt;br /&gt;
 sage: e.decimating_cipher()&lt;br /&gt;
 (x^9 + x^3 + 1, [0, 0, 1, 0, 0, 0, 1, 0, 1])&lt;br /&gt;
*** keystream_cipher(self)&lt;br /&gt;
 ...&lt;br /&gt;
 sage: e.keystream_cipher()&lt;br /&gt;
 (x^7 + x + 1, [0, 1, 0, 1, 0, 0, 0])&lt;br /&gt;
* sage.crypto.mq&lt;br /&gt;
** SBox Class: sage.crypto.mq.SBOX: see [http://wiki.sagemath.org/sage-2.10.4 wiki.sagemath.org]&lt;br /&gt;
*** S.difference_distribution_matrix()&lt;br /&gt;
*** S.maximal_difference_probability()&lt;br /&gt;
*** S.interpolation_polynomial()&lt;br /&gt;
*** S.polynomials(degree=2)&lt;br /&gt;
** Small Scale Variants of the AES (SR) Polynomial System Generator: cage.crypto.mq.sr [http://modular.math.washington.edu/sage/doc/html/ref/module-sage.crypto.mq.sr.html Reference Manual]&lt;br /&gt;
** Multivariate Polynomial Systems: sage.crypto.mq.mpolynomialsystem [http://modular.math.washington.edu/sage/doc/html/ref/module-sage.crypto.mq.mpolynomialsystem.html]&lt;br /&gt;
&lt;br /&gt;
===PyCrypto, the Python Cryptography Toolkit===&lt;br /&gt;
Sage ships [http://www.amk.ca/python/code/crypto PyCrypto] ([http://www.dlitz.net/software/pycrypto/ new maintainer&#039;s page?]) which implements many standard cryptographic algorithms. &amp;lt;br&amp;gt;It is not really meant for research/education/playing around but for production code but maybe something could be done to have easier access to it from within Sage. &amp;lt;br&amp;gt;The docstring level documentation is horrible:&lt;br /&gt;
 sage: import Crypto.Cipher.IDEA&lt;br /&gt;
 sage: Crypto.Cipher.IDEA?&lt;br /&gt;
    x.__init__(...) initializes x; see x.__class__.__doc__ for signature&lt;br /&gt;
&lt;br /&gt;
Manual is available [http://www.amk.ca/python/writing/pycrypt/pycrypt.html here].&amp;lt;br&amp;gt;&lt;br /&gt;
-&amp;gt; Contains also some info on how to extend the toolkit with new algorithms[http://www.amk.ca/python/writing/pycrypt/pycrypt.html#SECTION000900000000000000000]&amp;lt;br&amp;gt;&lt;br /&gt;
A blog about PyCrypto [http://pycrypto.blogspot.com here]&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
PyCrypto mostly consist of C code with a Python wrapper.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
INPUT/OUTPUT:&lt;br /&gt;
* always &amp;quot;binary strings&amp;quot; where each character represents 8 bits&amp;lt;br&amp;gt;&lt;br /&gt;
* except for RSA signatures -&amp;gt; those are &amp;quot;long&amp;quot;s&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;Hash functions&#039;&#039;&#039;([http://www.amk.ca/python/writing/pycrypt/pycrypt.html#SECTION000300000000000000000 Manual]): MD2, MD4, MD5, RIPEMD, SHA256, SHA, HMAC&lt;br /&gt;
** SHA1 example: [http://www.yobi.be:8300/home/pub/9 on Sage Notebook]&lt;br /&gt;
** HMAC example: [http://www.yobi.be:8300/home/pub/17/ on Sage Notebook]&lt;br /&gt;
** All hash functions support the API described by [http://www.python.org/dev/peps/pep-0247/ PEP 247]: after importing a given hashing module, call the &#039;&#039;&#039;new()&#039;&#039;&#039; function to create a new hashing object. You can now feed arbitrary strings into the object with the &#039;&#039;&#039;update()&#039;&#039;&#039; method, and can ask for the hash value at any time by calling the &#039;&#039;&#039;digest()&#039;&#039;&#039; or &#039;&#039;&#039;hexdigest()&#039;&#039;&#039; methods. The &#039;&#039;&#039;new()&#039;&#039;&#039; function can also be passed an optional string parameter that will be immediately hashed into the object&#039;s state.[http://www.amk.ca/python/writing/pycrypt/pycrypt.html#SECTION000300000000000000000]&amp;lt;br&amp;gt; Using the argument &#039;&#039;&#039;digest_size&#039;&#039;&#039; you can get the digest size but its constant.&lt;br /&gt;
** MD5, SHA and HMAC are just the standard Python implementations&lt;br /&gt;
** MD2, MD4, SHA256 and RIPEMD-160 are C implementations wrapped by PyCrypto&lt;br /&gt;
* &#039;&#039;&#039;Block encryption algorithms&#039;&#039;&#039;([http://www.amk.ca/python/writing/pycrypt/pycrypt.html#SECTION000400000000000000000 Manual]): AES, ARC2, Blowfish, CAST, DES, Triple-DES, IDEA, RC5, RC2&lt;br /&gt;
** AES &amp;amp; DES example: [http://www.yobi.be:8300/home/pub/7 on Sage Notebook]&lt;br /&gt;
** All block cipher support the interface described in [http://www.python.org/dev/peps/pep-0272/ PEP 272]&lt;br /&gt;
** Chaining modes: ECB, CBC, CFB, PGP, OFB and CTR&lt;br /&gt;
** Possibilities: Define a new cipher object after importing the module and define the key, mode (cbc,cfb,ecb or pgp) and possible IV.&amp;lt;br&amp;gt;The object gives you two methods: &#039;encrypt()&#039; and &#039;decrypt()&#039;.&amp;lt;br&amp;gt;For AES: S-Box not modifiable, LookUp Tables are being used. &lt;br /&gt;
* &#039;&#039;&#039;Stream encryption algorithms&#039;&#039;&#039;: ARC4, simple XOR&lt;br /&gt;
** RC4 example: [http://www.yobi.be:8300/home/pub/10/ on Sage Notebook]&lt;br /&gt;
** Support the interface described in [http://www.python.org/dev/peps/pep-0272/ PEP 272]&amp;lt;br&amp;gt; = block cipher with block size = 1&lt;br /&gt;
* &#039;&#039;&#039;Public-key algorithms&#039;&#039;&#039;([http://www.amk.ca/python/writing/pycrypt/pycrypt.html#SECTION000600000000000000000 Manual]): RSA, DSA, ElGamal, qNEW&lt;br /&gt;
** RSA example: [http://www.yobi.be:8300/home/pub/16/ on Sage Notebook]&lt;br /&gt;
** Signature is &#039;&#039;Long&#039;&#039; instead of &#039;&#039;Binary String&#039;&#039;&amp;lt;br&amp;gt;binascii doesn&#039;t provide long&amp;lt;-&amp;gt;binary conversion[http://mail.python.org/pipermail/python-3000/2006-November/004453.html]&amp;lt;br&amp;gt;Encrypted message is a &amp;quot;binary string&amp;quot;&lt;br /&gt;
** No PKCS#1 padding&amp;lt;br&amp;gt;&#039;&#039;sign&#039;&#039;() in RSA.py calls &#039;&#039;decrypt&#039;&#039;() and that only does &amp;quot;return pow(ciphertext[0], self.d, self.n)&amp;quot;&amp;lt;br&amp;gt;=&amp;gt; no padding &amp;lt;-&amp;gt; TLS Lite has padding&lt;br /&gt;
** n, e and d are also provided as &#039;&#039;Long&#039;&#039;&lt;br /&gt;
** Public key Modules&amp;lt;br&amp;gt;&#039;&#039;&#039;construct&#039;&#039;&#039;(tuple): construct( (long(n),long(e),long(d)) )&amp;lt;br&amp;gt;&#039;&#039;&#039;generate&#039;&#039;&#039;(size, randfunc, progress_func=None) =&amp;gt; public key object&lt;br /&gt;
**Public key Objects&amp;lt;br&amp;gt;available methods: &#039;&#039;&#039;canencrypt&#039;&#039;&#039;(), &#039;&#039;&#039;cansign&#039;&#039;&#039;(), &#039;&#039;&#039;decrypt&#039;&#039;&#039;(tuple), &#039;&#039;&#039;encrypt&#039;&#039;&#039;(string, K), &#039;&#039;&#039;hasprivate&#039;&#039;&#039;(), &#039;&#039;&#039;publickey&#039;&#039;&#039;(), &#039;&#039;&#039;sign&#039;&#039;&#039;(string, K), &#039;&#039;&#039;size&#039;&#039;&#039;(), &#039;&#039;&#039;verify&#039;&#039;&#039;(string, signature)&lt;br /&gt;
* &#039;&#039;&#039;Protocols&#039;&#039;&#039;: All-or-nothing transforms, chaffing/winnowing&lt;br /&gt;
* &#039;&#039;&#039;Miscellaneous&#039;&#039;&#039;:&lt;br /&gt;
** &#039;&#039;&#039;Crypto.Util.number&#039;&#039;&#039;&amp;lt;br&amp;gt;&#039;&#039;&#039;GCD&#039;&#039;&#039;(x,y),&#039;&#039;&#039;getPrime&#039;&#039;&#039;(N, randfunc),&#039;&#039;&#039;getRandomNumber&#039;&#039;&#039;(N, randfunc),&#039;&#039;&#039;inverse&#039;&#039;&#039;(u, v),&#039;&#039;&#039;isPrime&#039;&#039;&#039;(N)&lt;br /&gt;
** &#039;&#039;&#039;Crypto.Util.randpoo&#039;&#039;&#039;l[http://www.amk.ca/python/writing/pycrypt/pycrypt.html#SECTION000720000000000000000]&amp;lt;br&amp;gt;The randpool module implements a strong random number generator in the RandomPool class. The internal state consists of a string of random data, which is returned as callers request it. The class keeps track of the number of bits of entropy left, and provides a function to add new random data; this data can be obtained in various ways, such as by using the variance in a user&#039;s keystroke timings. &lt;br /&gt;
*** Getting N random bytes:&lt;br /&gt;
 sage: from Crypto.Util import randpool&lt;br /&gt;
 sage: randfunc = randpool.RandomPool()&lt;br /&gt;
 sage: randfunc.get_bytes(N)&lt;br /&gt;
 -&amp;gt; returns 8-bit string consisting of N bytes&lt;br /&gt;
* &#039;&#039;&#039;Some demo programs&#039;&#039;&#039; (currently all quite old and outdated)&lt;br /&gt;
&lt;br /&gt;
===OpenSSL===&lt;br /&gt;
[http://www.openssl.org/docs/crypto/crypto.html Manual] (incomplete, for example: no AES documentation)&amp;lt;br&amp;gt;&lt;br /&gt;
OpenSSL book on [http://books.google.com/books?id=FBYHEBTrZUwC Google Books]&amp;lt;br&amp;gt;&lt;br /&gt;
Functionality in OpenSLL:&lt;br /&gt;
* SYMMETRIC CIPHERS&lt;br /&gt;
:blowfish(3), cast(3), des(3), idea(3), rc2(3), rc4(3), rc5(3)&lt;br /&gt;
:Block Cipher Modes available: ECB, CBC, CFB, OFB&lt;br /&gt;
:Padding: only PKCS7 padding available?&lt;br /&gt;
:AES is same implementation as in pycrypto and only supports ECB and CBC (p. 175 in OpenSSL book)&lt;br /&gt;
* PUBLIC KEY CRYPTOGRAPHY AND KEY AGREEMENT&lt;br /&gt;
:dsa(3), dh(3), rsa(3)&lt;br /&gt;
* CERTIFICATES&lt;br /&gt;
:x509(3), x509v3(3)&lt;br /&gt;
* AUTHENTICATION CODES, HASH FUNCTIONS&lt;br /&gt;
:hmac(3), md2(3), md4(3), md5(3), mdc2(3), ripemd(3), sha(3)&lt;br /&gt;
:CBC-MAC and XCBC-MAC algorithms for OpenSSL are provided [http://books.google.com/books?id=FBYHEBTrZUwC&amp;amp;pg=PA205&amp;amp;lpg=PA205&amp;amp;dq=cbc-mac+openssl&amp;amp;source=web&amp;amp;ots=Am1o_i-tMJ&amp;amp;sig=Abx0hmynfuKMcrbgJH1qhoC1M3w&amp;amp;hl=en&amp;amp;sa=X&amp;amp;oi=book_result&amp;amp;resnum=1&amp;amp;ct=result#PPA205,M1 here].&lt;br /&gt;
* AUXILIARY FUNCTIONS&lt;br /&gt;
:err(3), threads(3), rand(3), OPENSSL_VERSION_NUMBER(3)&lt;br /&gt;
* INPUT/OUTPUT, DATA ENCODING&lt;br /&gt;
:asn1(3), bio(3), evp(3), pem(3), pkcs7(3), pkcs12(3)&lt;br /&gt;
* INTERNAL FUNCTIONS&lt;br /&gt;
:bn(3), buffer(3), lhash(3), objects(3), stack(3),txt_db(3)&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Functionality of OpenSSL in Sage is provided via the PyOpenSSL wrapper. A more complete wrapper is M2Crypto but it is not available as a package for Sage. Still have to try to import it&lt;br /&gt;
====PyOpenSSL====&lt;br /&gt;
http://pyopenssl.sourceforge.net/pyOpenSSL.html/&lt;br /&gt;
*X509 objects&lt;br /&gt;
*509Name objects&lt;br /&gt;
*X509Req objects&lt;br /&gt;
*:X509Store objects&lt;br /&gt;
*PKey objects&lt;br /&gt;
*PKCS7 objects&lt;br /&gt;
*PKCS12 objects&lt;br /&gt;
*X509Extension objects&lt;br /&gt;
*NetscapeSPKI objects&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Looks like less functionality than PyCrypto =&amp;gt; PyCrypto seems like a better candidate to adjust, else we would have to extend the PyOpenSSL wrapper AND OpenSSL itself for any wanted extended functionality.&lt;br /&gt;
====M2Crypto====&lt;br /&gt;
[http://chandlerproject.org/Projects/MeTooCrypto Homepage]&amp;lt;br&amp;gt;&lt;br /&gt;
[http://www.pktek.net/docs/python/M2Crypto/M2Crypto-module.html API documentation]&amp;lt;br&amp;gt;&lt;br /&gt;
Oreilly: OpenSSL p. 258-266&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
INPUT/OUTPUT:&lt;br /&gt;
* always &amp;quot;binary strings&amp;quot; where each character represents 8 bits&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;symmetric ciphers&#039;&#039;&#039; (in EVP module: AES, Blowfish, CAST5, DES, DESX, 3DES, IDEA, RC2, RC4, RC5)&lt;br /&gt;
** AES example: [http://www.yobi.be:8300/home/pub/8/ on Sage Notebook]&lt;br /&gt;
** RC4 example: [http://www.yobi.be:8300/home/pub/12/ on Sage Notebook]&lt;br /&gt;
*** other algo&#039;s that can be used:&amp;lt;br&amp;gt;aes_128_x, aes_192_x, aes_256_x, bf_x, cast_x, des_x, desx_cbc, des_ede3(_x), des(_ede_x), idea_x, rc2_x, rc4(_40), rc5_32_16_12_x&amp;lt;br&amp;gt;Where x is the chaining mode and (...) is optional&lt;br /&gt;
** EVP module (&#039;&#039;from M2Crypto import EVP&#039;&#039;)= message digests, symmetric ciphers and PK algo&#039;s&lt;br /&gt;
** PKCS7 padding is used&lt;br /&gt;
** A Cipher and Message Digest example:&amp;lt;br&amp;gt;see [http://books.google.com/books?id=IIqwAy4qEl0C&amp;amp;pg=PA261&amp;amp;lpg=PA261&amp;amp;dq=EVP.MessageDigest&amp;amp;source=web&amp;amp;ots=m9061S6wTx&amp;amp;sig=WE-Sqje8LMCwpA370wACq1iROlo&amp;amp;hl=en&amp;amp;sa=X&amp;amp;oi=book_result&amp;amp;resnum=1&amp;amp;ct=result#PPA261,M1 here]&lt;br /&gt;
* &#039;&#039;&#039;message digests&#039;&#039;&#039; (MD5, SHA1, RipeMD-160) (in EVP module)&lt;br /&gt;
** SHA1 example: [http://www.yobi.be:8300/home/pub/9/ on Sage Notebook]&lt;br /&gt;
* &#039;&#039;&#039;HMAC&#039;&#039;&#039;&lt;br /&gt;
** example: [http://www.yobi.be:8300/home/pub/15/ on Sage Notebook]&lt;br /&gt;
** 2 possibilities: using the HMAC class or the &#039;&#039;hmac()&#039;&#039; function&lt;br /&gt;
** supports following hash functions: MD2/4/5,MDC2,SHA1,RipeMD-160&amp;lt;br&amp;gt;&#039;&#039;TLS Lite allows any hash functions conform to PEP 247 to be used&#039;&#039;&lt;br /&gt;
** the API is not conform to the PEP 247 specifications&amp;lt;br&amp;gt;&#039;&#039;The TLS Lite implementation is conform to PEP 247&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;RSA, DSA, DH&#039;&#039;&#039; (in EVP module)&lt;br /&gt;
** See OpenSSL book [http://books.google.com/books?id=FBYHEBTrZUwC&amp;amp;pg=PA263&amp;amp;vq=%22from+M2Crypto+import+DH,+DSA,+RSA,+RC4%22&amp;amp;source=gbs_search_r&amp;amp;cad=1_1&amp;amp;sig=ACfU3U2PSbjVhJdo2R3Osyz1zcRM8fx7Dw on google books]&lt;br /&gt;
* &#039;&#039;&#039;PKCS7 padding&#039;&#039;&#039;&lt;br /&gt;
** example: [http://www.yobi.be:8300/home/pub/18/ on Sage Notebook]&lt;br /&gt;
* SSL functionality to implement clients and servers.&lt;br /&gt;
* HTTPS extensions to Python&#039;s httplib, urllib, and xmlrpclib.&lt;br /&gt;
* Unforgeable HMAC&#039;ing AuthCookies for web session management.&lt;br /&gt;
* FTP/TLS client and server.&lt;br /&gt;
* S/MIME.&lt;br /&gt;
* ZServerSSL: A HTTPS server for Zope.&lt;br /&gt;
* ZSmime: An S/MIME messenger for Zope.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
More functionality than the PyOpenSSL wrapper, but not available as a Sage package. Importing in sage is easy.&lt;br /&gt;
=====Setup and import=====&lt;br /&gt;
 sudo aptitude install openssl libssl-dev python-dev&lt;br /&gt;
 &#039;&#039;&lt;br /&gt;
 $ python setup.py build&lt;br /&gt;
 $ python setup.py install&lt;br /&gt;
 &#039;&#039;&lt;br /&gt;
 sage: import sys                                        &lt;br /&gt;
 sage: sys.path.append(&#039;/usr/lib/python2.5/site-packages&#039;)&lt;br /&gt;
 sage: import M2Crypto&lt;br /&gt;
or&lt;br /&gt;
 $ sage -python setup.py install&lt;br /&gt;
&lt;br /&gt;
===GnuTLS===&lt;br /&gt;
[http://www.gnu.org/software/gnutls/manual/html_node/index.html Manual]&amp;lt;br&amp;gt;&lt;br /&gt;
Standard package in sage.&amp;lt;br&amp;gt;&lt;br /&gt;
Mostly for certification and not for basic cryptography.&amp;lt;br&amp;gt;&lt;br /&gt;
*Support for TLS 1.1, TLS 1.0 and SSL 3.0 protocols&lt;br /&gt;
:Since SSL 2.0 is insecure it is not supported.&lt;br /&gt;
:TLS 1.2 is supported but disabled by default.&lt;br /&gt;
*Support for TLS extensions: server name indication, max record size, opaque PRF input, etc.&lt;br /&gt;
*Support for authentication using the SRP protocol.&lt;br /&gt;
*Support for authentication using both X.509 certificates and OpenPGP keys.&lt;br /&gt;
*Support for TLS Pre-Shared-Keys (PSK) extension.&lt;br /&gt;
*Support for Inner Application (TLS/IA) extension.&lt;br /&gt;
*Support for X.509 and OpenPGP certificate handling.&lt;br /&gt;
*Support for X.509 Proxy Certificates (RFC 3820).&lt;br /&gt;
*Supports all the strong encryption algorithms (including SHA-256/384/512), including Camellia (RFC 4132).&lt;br /&gt;
*Supports compression.&lt;br /&gt;
===Python-GnuTLS===&lt;br /&gt;
http://pypi.python.org/pypi/python-gnutls/1.1.5&amp;lt;br&amp;gt;&lt;br /&gt;
API reference built on local machine.&amp;lt;br&amp;gt;&lt;br /&gt;
Same story as with OpenSSL: C-library + python wrapper&amp;lt;br&amp;gt;&lt;br /&gt;
===TLS Lite===&lt;br /&gt;
[http://trevp.net/tlslite/ Homepage]&amp;lt;br&amp;gt;&lt;br /&gt;
[http://sourceforge.net/mailarchive/forum.php?forum_name=tlslite-users Mailing List Archive]&amp;lt;br&amp;gt;&lt;br /&gt;
TLS Lite hasn&#039;t been updated since February 21, 2005&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
INPUT/OUTPUT:&lt;br /&gt;
* always &amp;quot;binary strings&amp;quot; where each character represents 8 bits&lt;br /&gt;
* exception for RSA: &amp;quot;array of bytes&amp;quot; instead of &amp;quot;binary strings&amp;quot;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Not available as Sage package, but it is &#039;&#039;&#039;&amp;lt;u&amp;gt;pure&amp;lt;/u&amp;gt; python&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;quot;TLS Lite is a free python library that implements SSL 3.0, TLS 1.0, and TLS 1.1. TLS Lite supports non-traditional authentication methods such as SRP, shared keys, and cryptoIDs in addition to X.509 certificates. TLS Lite is pure Python, however it can access OpenSSL, cryptlib, pycrypto, and GMPY for faster crypto operations. TLS Lite integrates with httplib, xmlrpclib, poplib, imaplib, smtplib, SocketServer, asyncore, and Twisted.&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
Pure python implementations for:&lt;br /&gt;
*&#039;&#039;&#039;AES&#039;&#039;&#039;: &lt;br /&gt;
**example: [http://www.yobi.be:8300/home/pub/6 on Sage Notebook]&lt;br /&gt;
**Interesting are AES.py, Python_AES.py and rijndael.py&amp;lt;br&amp;gt;&lt;br /&gt;
***rijndael.py originally [http://bitconjurer.org/ here] and was ported from [http://www.quadcap.com/products/qed/docs/source/_rijndael_8java-source.html this] java code&lt;br /&gt;
***Might be possible to modify SBox but &#039;&#039;&#039;only CBC-mode&#039;&#039;&#039; available (see Python_AES.py)&lt;br /&gt;
** No padding&lt;br /&gt;
*&#039;&#039;&#039;RC4&#039;&#039;&#039;&lt;br /&gt;
** example: [http://www.yobi.be:8300/home/pub/11/ on Sage Notebook]&lt;br /&gt;
*&#039;&#039;&#039;RSA&#039;&#039;&#039;&lt;br /&gt;
** example: [http://www.yobi.be:8300/home/pub/13/ on Sage Notebook]&lt;br /&gt;
** signature = PKCS1-SHA1&lt;br /&gt;
** input/output is array of bytes instead of binary string&amp;lt;br&amp;gt;Use &#039;&#039;tlslite.utils.keyfactory.stringToBytes&#039;&#039; and &#039;&#039;tlslite.utils.keyfactory.bytesToString&#039;&#039; to convert between array of bytes and binary string&lt;br /&gt;
*&#039;&#039;&#039;TripleDES&#039;&#039;&#039;&lt;br /&gt;
** no pure python implementation. API available for cryptlib, openssl(m2crypto) and pycrypto&lt;br /&gt;
*&#039;&#039;&#039;HMAC&#039;&#039;&#039;&lt;br /&gt;
** example: [http://www.yobi.be:8300/home/pub/14/ on Sage Notebook]&lt;br /&gt;
** supports the API for Cryptographic Hash Functions ([http://www.python.org/dev/peps/pep-0247/ PEP 247])&lt;br /&gt;
** can use any hashing algorithm that is also conform to the PEP 247 specifications&lt;br /&gt;
** source has some comments: tlslite/utils/hmac.py&lt;br /&gt;
*tlslite.utils.cryptomath. ... interesting?&amp;lt;br&amp;gt;base64ToBytes base64ToNumber base64ToString bytesToBase64 bytesToNumber gcd getBase64Nonce getRandomBytes getRandomNumber getRandomPrime getRandomSafePrime hashAndBase64 invMod isPrime lcm makeSieve mpiToNumber numberToBase64 numberToBytes  numberToMPI numberToString numBytes powMod stringToBase64 stringToNumber&lt;br /&gt;
&lt;br /&gt;
====Remarks====&lt;br /&gt;
=====Bug=====&lt;br /&gt;
When calling the key generation function &amp;quot;rsa = tlslite.utils.keyfactory.generateRSAKey(384,[&amp;quot;python&amp;quot;])&amp;quot; it will throw an error. The TLS Lite code will also break in future versions of python (more info on the SourceForge link)&amp;lt;br&amp;gt;&lt;br /&gt;
Solutions:&lt;br /&gt;
* add an &amp;quot;r&amp;quot; to the number to cast it to a python Integer instead of a Sage integer.&amp;lt;br&amp;gt;=&amp;gt; rsa = tlslite.utils.keyfactory.generateRSAKey(384r,[&amp;quot;python&amp;quot;])&lt;br /&gt;
* fix the code of TLS Lite:&amp;lt;br&amp;gt;see the bugreport on [http://sourceforge.net/tracker/index.php?func=detail&amp;amp;aid=2027766&amp;amp;group_id=102605&amp;amp;atid=632288 SourceForge]&lt;br /&gt;
=====Importing in Sage=====&lt;br /&gt;
 sage: import sys                                        &lt;br /&gt;
 sage: sys.path.append(&#039;/usr/lib/python2.5/site-packages&#039;)&lt;br /&gt;
 sage: import tlslite&lt;br /&gt;
 sage: from tlslite.api import *&lt;br /&gt;
&lt;br /&gt;
===Python===&lt;br /&gt;
*conversion between &amp;quot;binary string&amp;quot; and &amp;quot;hexadecimal string&amp;quot;&lt;br /&gt;
** convert hexadecimal to appropriate string input via:&amp;lt;br&amp;gt;&#039;&#039;sage: &amp;quot;A0B1C2&amp;quot;.decode(&#039;hex&#039;)&#039;&#039;&lt;br /&gt;
** convert string output to hexadecimal via:&amp;lt;br&amp;gt;&#039;&#039;sage: &amp;quot;\xA0\xB1\xC2&amp;quot;.encode(&#039;hex&#039;)&#039;&#039;&lt;br /&gt;
* modules: hmac, md5, sha, hashlib[http://docs.python.org/lib/module-hashlib.html] (contains: md5(), sha1(), sha224(), sha256(), sha384(), and sha512())&lt;br /&gt;
&lt;br /&gt;
===Misc===&lt;br /&gt;
*[http://echidna.maths.usyd.edu.au/~kohel/tch/Crypto/index.html book written on Cryptography by David Kohel], using SAGE&lt;br /&gt;
*[http://twhiteman.netfirms.com/des.html pyDes]: python implementation of DES and 3DES&lt;br /&gt;
*[http://www.cryptool.org/ Cryptool]: open source windows program for educational use of cryptography&lt;br /&gt;
*[http://www.libtom.org LibtomCrypt]: C library with lots of stuff with good documentation&lt;br /&gt;
*[https://sourceforge.net/projects/cryptopy/ CryptoPy]: has some code to analyze SBox&amp;lt;br&amp;gt; AES Sbox Analysis - a simple analysis of the AES Sbox that determines the number and size of the permutation subgroups in the transformation. Could be extended to examine any Sbox ...&lt;br /&gt;
*[http://www.stuvel.eu/rsa RSA implementation in Python]&lt;br /&gt;
* [http://jclement.ca/software/pyrijndael/ PyRijndael]&lt;br /&gt;
** based on Phil Fresle&#039;s VB implementation [http://www.frez.co.uk/freecode.htm#rijndael] (doesn&#039;t provide more comments)&lt;br /&gt;
** two high level functions:&lt;br /&gt;
*** &#039;&#039;EncryptData&#039;&#039;(key, data): Encrypts data using key and returns encrypted string.  Uses 256 bit Rijndael cipher.  Key is built from first 32 characters of password.  A 32-bit message length is attached to beginning of ciphertext.&lt;br /&gt;
*** &#039;&#039;DecryptData&#039;&#039;(key, data)&lt;br /&gt;
** example in the code:&lt;br /&gt;
    PlainText=&amp;quot;Hello World&amp;quot; *50&lt;br /&gt;
    Key=&amp;quot;Secret&amp;quot;&lt;br /&gt;
    CipherText=EncryptData(Key,PlainText)&lt;br /&gt;
    PlainText2=DecryptData(Key,CipherText)&lt;br /&gt;
    print &amp;quot;PT :&amp;quot;,PlainText&lt;br /&gt;
    print &amp;quot;KY :&amp;quot;,Key&lt;br /&gt;
    print &amp;quot;PT2:&amp;quot;,PlainText2&lt;br /&gt;
* Collection of Python Crypto stuff: [http://vermeulen.ca/python-cryptography.html here]&lt;br /&gt;
** PBKDF2 pure python implementation + example: [http://code.google.com/p/uliweb/source/browse/trunk/lib/beaker/crypto/pbkdf2.py?r=119 here]&lt;br /&gt;
** Public Key algo&#039;s in pure python: [http://pypi.python.org/pypi/asym/0.1.0 here]&lt;br /&gt;
*in Python:&lt;br /&gt;
** Python Enhancement Proposals&lt;br /&gt;
*** [http://www.python.org/dev/peps/pep-0247/ PEP 247]: API for Cryptographic Hash Functions&lt;br /&gt;
*** [http://www.python.org/dev/peps/pep-0272/ PEP 272]: API for Block Encryption Algorithms v1.0&lt;br /&gt;
*** [http://www.python.org/dev/peps/pep-0358/ PEP 358]: The &amp;quot;bytes&amp;quot; Object (not implemented yet)&lt;br /&gt;
*** [http://www.python.org/dev/peps/pep-3137/ PEP 3137]: Immutable Bytes and Mutable Buffer (not implemented yet)&lt;br /&gt;
**Random generators &lt;br /&gt;
*** module [http://docs.python.org/tut/node12.html#SECTION0012600000000000000000 random] (present in Sage)&lt;br /&gt;
*** module [http://docs.python.org/tut/node17.html#SECTION0017310000000000000000 _random] (Mersenne Twister) (present in Sage)&lt;br /&gt;
*** os.urandom(n) (present in Sage)&lt;br /&gt;
*** numpy.random core random tools of NumPy (present in Sage) (also via scipy.random)&lt;br /&gt;
** Resources for Python modules related to crypto:&lt;br /&gt;
*** [http://pypi.python.org/pypi?%3Aaction=search&amp;amp;term=crypt&amp;amp;submit=search Python Package Index]&lt;br /&gt;
*** [http://sourceforge.net/search/index.php?words=(%2Bpython+%2Bcrypt)&amp;amp;type_of_search=soft&amp;amp;pmode=0&amp;amp;words=(%2Bpython+%2Bcrypto)&amp;amp;Search=Search on SourceForge]&lt;br /&gt;
*** [http://py.vaults.ca/apyllo.py?find=crypt Vaults of Parnassus]&lt;br /&gt;
** [http://wiki.python.org/moin/CodingProjectIdeas/Libraries Python Coding Project Ideas]: M2Crypto &amp;amp; TLS Lite are mentioned there&lt;br /&gt;
* Blockcipher API for ECB, CBC, CFB &amp;amp; OFB: [http://www.nightsong.com/phr/crypto/blockcipher.tgz] &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;To be looked at:&#039;&#039;&#039;&lt;br /&gt;
* Python code for ripemd (PEP 247), rijndael, serpent, twofish, whirlpool (PEP 247), XTS&lt;br /&gt;
** code: [http://psionicist.online.fr/code/ here]&lt;br /&gt;
** blog explaining TrueCrypt using that code: [http://blog.bjrn.se/2008/01/truecrypt-explained.html here]&lt;br /&gt;
* [http://labix.org/python-mcrypt Python-mcrypt]&amp;lt;br&amp;gt; python-mcrypt is a comprehensive Python interface to the mcrypt library, which is a library providing a uniform interface to several symmetric encryption algorithms. It is intended to have a simple interface to access encryption algorithms in ofb, cbc, cfb, ecb and stream modes. The algorithms it supports are DES, 3DES, RIJNDAEL, Twofish, IDEA, GOST, CAST-256, ARCFOUR, SERPENT, SAFER+, and more.&lt;br /&gt;
* [http://del.icio.us/tag/python%2Bcrypto on Del.icio.us]&lt;br /&gt;
&lt;br /&gt;
==Wishes==&lt;br /&gt;
* [http://www.sagemath.org:9002/sage_trac/report/1?sort=ticket&amp;amp;asc=0 General trac]&lt;br /&gt;
* sage.crypto: block ciphers&lt;br /&gt;
* Someone needs to replace FiniteField_ext_pari with the two NTL implementations (they are much faster).&lt;br /&gt;
* elliptic and hyperelliptic curves over finite fields support is rather poor&lt;br /&gt;
* algebraic aspects received some attention for the cryptanalysis of symmetric cryptographic algorithms, i.e. the cryptanalyst expresses the cipher as a large set of multivariate polynomials and attempts to solve the system. The most common case over GF(2) is handled by PolyBoRi. This library is the backbone of BooleanPolynomialRing and friends. This class needs testing, documentation, extension and bugfixes. Basically someone should sit down and add all the methods of MPolynomial[Ring]_libsingular to BooleanPolynomial[Ring] which make sense, add a ton of doctests and test the hell out of the library to make sure no SIGSEGVs surprise the user.&lt;br /&gt;
* the module sage.crypto.mq is also relevant for the above.&lt;br /&gt;
* Univariate polynomials over GF(2) are still implemented via NTL&#039;s ZZ_pX class rather than GF2X. This should be changed. Also [http://trac.sagemath.org/sage_trac/ticket/2114 this ticket] has a link to gf2x a very small drop in replacement C library which claimed to be 5x faster than NTL.  Though, a formal vote is needed to get it into Sage.&lt;br /&gt;
* At the end of the day everything boils down to linear algebra. So if you improve that, everybody wins. Sparse linear algebra mod p is still too slow (Ralf-Phillip Weinmann did some work here wrapping code from eclib), there isn o special implementation for sparse linear algebra over GF(2) (both blackbox and e.g. reduced echelon forms), dense LA over GF(2) needs Strassen multiplication/reduction, dense LA over GF(2^n) should probably get implemented.&lt;br /&gt;
==[[Sage ideal crypto toolbox|The ideal toolbox]]==&lt;br /&gt;
&lt;br /&gt;
==[[Sage Cross Reference Table of Wishes and Availability | Cross Reference Table: wishes &amp;lt;-&amp;gt; availability]]==&lt;br /&gt;
==[[Sage Sandbox]]==&lt;br /&gt;
==[[PyCryptoPlus]]==&lt;/div&gt;</summary>
		<author><name>Newacct</name></author>
	</entry>
	<entry>
		<id>https://wiki.yobi.be/index.php?title=Sage_Sandbox&amp;diff=6048</id>
		<title>Sage Sandbox</title>
		<link rel="alternate" type="text/html" href="https://wiki.yobi.be/index.php?title=Sage_Sandbox&amp;diff=6048"/>
		<updated>2009-12-24T06:56:55Z</updated>

		<summary type="html">&lt;p&gt;Newacct: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Back to [[SAGE &amp;amp; cryptology]]&lt;br /&gt;
&lt;br /&gt;
==Helper functions==&lt;br /&gt;
Conversion from int or long to raw string and back is [http://bugs.python.org/issue1023290 discussed on the Python bug tracking list] but since the problem is not yet solved, let&#039;s see how to make our own helper functions in a clean and fast way: (measures with %timeit of ipython)&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
###############################  long2string  ########################################&lt;br /&gt;
def long2string(i):&lt;br /&gt;
	l=[]&lt;br /&gt;
	while i:&lt;br /&gt;
		l[0:0]=chr(i&amp;amp;0xff)&lt;br /&gt;
		i&amp;gt;&amp;gt;=8&lt;br /&gt;
	return &#039;&#039;.join(l)&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#46 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#51.8 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
def long2string(i):&lt;br /&gt;
	l=[]&lt;br /&gt;
	while i:&lt;br /&gt;
		l.append(chr(i&amp;amp;0xff))&lt;br /&gt;
		i&amp;gt;&amp;gt;=8&lt;br /&gt;
	return &#039;&#039;.join(l[::-1])&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#15.1 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#16.4 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
def long2string(i): &lt;br /&gt;
        return &#039;&#039;.join([chr(i&amp;gt;&amp;gt;j &amp;amp; 0xFF) for j in range(int(math.log(i,2)//8)&amp;lt;&amp;lt;3,-1,-8)])&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#15.5 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#16.7 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
import math&lt;br /&gt;
def long2string(i):&lt;br /&gt;
	return (&#039;%0*x&#039; % (int(math.ceil(math.log(i,2)/8)*2) , i)).decode(&#039;hex&#039;)&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#4.77 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#4.83 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
def long2string(i):&lt;br /&gt;
	return (&#039;%0*x&#039; % (len(hex(i)) &amp;amp; -2 , i)).decode(&#039;hex&#039;).lstrip(&#039;\x00&#039;)&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#3.64 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#3.78 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
(&#039;%0*x&#039; % (32 , 123456789012345678901234)).decode(&#039;hex&#039;)&lt;br /&gt;
#2.06 µs per loop&lt;br /&gt;
(&#039;%0*x&#039; % (32 , 1234567890123456789012345)).decode(&#039;hex&#039;)&lt;br /&gt;
#2.01 µs per loop&lt;br /&gt;
#Ok if you want fixed length buffer, e.g. 32 bytes&lt;br /&gt;
###################################################################################&lt;br /&gt;
def long2string(i):&lt;br /&gt;
    s=hex(i)[2:].rstrip(&#039;L&#039;)&lt;br /&gt;
    if len(s) % 2:&lt;br /&gt;
        s=&#039;0&#039;+s&lt;br /&gt;
    return s.decode(&#039;hex&#039;)&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#2.88 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#3.22 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
def long2string(i):&lt;br /&gt;
    s=&#039;0&#039;+hex(long(i))[2:-1]&lt;br /&gt;
    return s[len(s) % 2:].decode(&#039;hex&#039;)&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#3.04 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#3.03 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
def long2string(i):&lt;br /&gt;
    s=hex(long(i))[2:-1]&lt;br /&gt;
    if len(s) % 2:&lt;br /&gt;
        s=&#039;0&#039;+s&lt;br /&gt;
    return s.decode(&#039;hex&#039;)&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#2.75 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#3.06 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
#If we are sure our argument is a long we can remove the cast in the 2 previous codes:&lt;br /&gt;
def long2string(i):&lt;br /&gt;
    s=&#039;0&#039;+hex(i)[2:-1]&lt;br /&gt;
    return s[len(s) % 2:].decode(&#039;hex&#039;)&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#2.66 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#2.62 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
def long2string(i):&lt;br /&gt;
    s=hex(i)[2:-1]&lt;br /&gt;
    if len(s) % 2:&lt;br /&gt;
        s=&#039;0&#039;+s&lt;br /&gt;
    return s.decode(&#039;hex&#039;)&lt;br /&gt;
long2string(123456789012345678901234)&lt;br /&gt;
#2.42 µs per loop&lt;br /&gt;
long2string(1234567890123456789012345)&lt;br /&gt;
#2.76 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
# There is still room ;-)&lt;br /&gt;
&#039;1a249b1f10a06c96aff2&#039;.decode(&#039;hex&#039;)&lt;br /&gt;
#990 ns per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
###############################  string2long  ########################################&lt;br /&gt;
def string2long(s):&lt;br /&gt;
	i=0&lt;br /&gt;
	for c in s:&lt;br /&gt;
		i=(i&amp;lt;&amp;lt;8)+ord(c)&lt;br /&gt;
	return i&lt;br /&gt;
string2long(&#039;\x01\x05n\x0f6\xa6D=\xe2\xdfy&#039;)&lt;br /&gt;
#8.91 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
int(&#039;\x01\x05n\x0f6\xa6D=\xe2\xdfy&#039;.encode(&#039;hex&#039;),16)&lt;br /&gt;
#3.15 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
int(&#039;\x01\x05n\x0f6\xa6D=\xe2\xdfy&#039;.decode(&#039;hex&#039;),16)&lt;br /&gt;
#1.64 µs per loop&lt;br /&gt;
###################################################################################&lt;br /&gt;
long(&#039;\x01\x05n\x0f6\xa6D=\xe2\xdfy&#039;.decode(&#039;hex&#039;),16)&lt;br /&gt;
#1.38 µs per loop&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Learning Python==&lt;br /&gt;
Some code to make some padding functions available. The code is used as a way to learn python, so it contains things that don&#039;t make sense or could be done better.&lt;br /&gt;
&lt;br /&gt;
{{#fileanchor: helper.py}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
from __future__ import division #http://www.python.org/dev/peps/pep-0238/&lt;br /&gt;
import math&lt;br /&gt;
&lt;br /&gt;
def roundUp (n, p):&lt;br /&gt;
	&amp;quot;&amp;quot;&amp;quot;Round an integer up to the nearest multiple&lt;br /&gt;
&lt;br /&gt;
	A given integer n will be round up to the nearest multiple of p&lt;br /&gt;
&lt;br /&gt;
	Example:&lt;br /&gt;
	&amp;gt;&amp;gt;&amp;gt; roundUp(13,8)&lt;br /&gt;
	    16&lt;br /&gt;
	&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
	return int(math.ceil(n/p)*p)&lt;br /&gt;
	#return (n+p)/p*p	&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Download code: [{{#filelink: helper.py}} helper.py]&lt;br /&gt;
&lt;br /&gt;
{{#fileanchor: padding.py}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import random&lt;br /&gt;
&lt;br /&gt;
import sys&lt;br /&gt;
from optparse import OptionParser&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
	import helper&lt;br /&gt;
except ImportError:&lt;br /&gt;
	print &#039;ERROR: helper.py should be placed in the same location as padding.py&#039;&lt;br /&gt;
	raise ImportError&lt;br /&gt;
&lt;br /&gt;
class BlockOperator:&lt;br /&gt;
	&amp;quot;&amp;quot;&amp;quot;Class for BlockOperator&lt;br /&gt;
&lt;br /&gt;
	Only holds a variable blocksize. Class only exist for testing purpose.&lt;br /&gt;
	&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	def __init__(self, bs=8):&lt;br /&gt;
		self.blockSize = bs&lt;br /&gt;
&lt;br /&gt;
class Padding(BlockOperator):&lt;br /&gt;
	&amp;quot;&amp;quot;&amp;quot;Class for padding functions&lt;br /&gt;
&lt;br /&gt;
	Inherits from the BlockOperator class&lt;br /&gt;
&lt;br /&gt;
	padding info here: http://en.wikipedia.org/wiki/Padding_(cryptography)&lt;br /&gt;
&lt;br /&gt;
	Example:&lt;br /&gt;
		from padding import *&lt;br /&gt;
		padder = Padding()&lt;br /&gt;
		padded = padder.pad(&#039;test&#039;,&#039;bitpadding)&lt;br /&gt;
		padder.unpad(padded,&#039;bitpadding)&lt;br /&gt;
&lt;br /&gt;
	After changing the source:&lt;br /&gt;
		del padder&lt;br /&gt;
		del Padding&lt;br /&gt;
		from padding import *&lt;br /&gt;
	&amp;quot;&amp;quot;&amp;quot;		&lt;br /&gt;
&lt;br /&gt;
	def pad (self, toPad, algo):&lt;br /&gt;
		#private function has to be accessed by it&#039;s public name when using getattr...&lt;br /&gt;
		return getattr(self,&amp;quot;_%(classname)s__%(algo)s&amp;quot; % {&#039;classname&#039;:self.__class__.__name__,&#039;algo&#039;:algo})(toPad) &lt;br /&gt;
&lt;br /&gt;
	def unpad (self, padded, algo):&lt;br /&gt;
		#there is no switch statement in python&lt;br /&gt;
		#discussion with alternatives: http://simonwillison.net/2004/May/7/switch/&lt;br /&gt;
		if algo == &#039;bitPadding&#039;:&lt;br /&gt;
			return self.__bitPadding_unpad(padded)&lt;br /&gt;
		elif algo == &#039;zerosPadding&#039;:&lt;br /&gt;
			return self.__zerosPadding_unpad(padded)&lt;br /&gt;
		elif algo == &#039;PKCS7&#039;:&lt;br /&gt;
			return self.__PKCS7_unpad(padded)&lt;br /&gt;
		elif algo == &#039;ANSI_X923&#039;:&lt;br /&gt;
			return self.__ANSI_X923_unpad(padded)&lt;br /&gt;
		elif algo == &#039;ISO_10126&#039;:&lt;br /&gt;
			return self.__ISO_10126_unpad(padded)&lt;br /&gt;
		raise NotImplementedError()&lt;br /&gt;
&lt;br /&gt;
	def __bitPadding (self, toPad ):&lt;br /&gt;
		padded = toPad + &#039;\x80&#039; + &#039;\x00&#039;*(self.blockSize - len(toPad)%self.blockSize -1)&lt;br /&gt;
		return padded&lt;br /&gt;
&lt;br /&gt;
	def __bitPadding_unpad (self, padded ):&lt;br /&gt;
		if padded.rstrip(&#039;\x00&#039;)[-1] == &#039;\x80&#039;:&lt;br /&gt;
			return padded.rstrip(&#039;\x00&#039;)[:-1]&lt;br /&gt;
		else:&lt;br /&gt;
			return padded&lt;br /&gt;
&lt;br /&gt;
	def __zerosPadding (self, toPad ):&lt;br /&gt;
		totalLength = helper.roundUp(len(toPad),self.blockSize)&lt;br /&gt;
		return toPad.ljust(totalLength,&#039;\x00&#039;)&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
	def __zerosPadding_unpad (self, padded ):&lt;br /&gt;
		return padded.rstrip(&#039;\x00&#039;)		&lt;br /&gt;
&lt;br /&gt;
	def __PKCS7 (self, toPad ):&lt;br /&gt;
		&amp;quot;&amp;quot;&amp;quot;Pad a binary string&lt;br /&gt;
&lt;br /&gt;
		Input:&lt;br /&gt;
			toPad: binary string to be padded&lt;br /&gt;
			self.blockSize: the padded result will be a multiple of the self.blockSize(default=8)&lt;br /&gt;
		Output:&lt;br /&gt;
			return a binary string&lt;br /&gt;
		&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		pattern = self.blockSize - len(toPad)%self.blockSize&lt;br /&gt;
		patternstring = chr(pattern)		&lt;br /&gt;
		amount = self.blockSize - len(toPad)%self.blockSize&lt;br /&gt;
		pad = patternstring * amount&lt;br /&gt;
		return toPad + pad&lt;br /&gt;
&lt;br /&gt;
	def __PKCS7_unpad (self, padded ):&lt;br /&gt;
		pattern = padded[-1]&lt;br /&gt;
		length = ord(pattern)&lt;br /&gt;
		#check if the bytes to be removed are all the same pattern&lt;br /&gt;
		if padded.endswith(pattern*length):&lt;br /&gt;
			return padded[:-length]&lt;br /&gt;
		else:&lt;br /&gt;
			return padded&lt;br /&gt;
			print &#039;error: padding pattern not recognized&#039;&lt;br /&gt;
&lt;br /&gt;
	def __ANSI_X923 (self, toPad ):&lt;br /&gt;
		bytesToPad = self.blockSize - len(toPad)%self.blockSize&lt;br /&gt;
		pattern = &#039;\x00&#039;*(bytesToPad -1) + chr(bytesToPad)&lt;br /&gt;
		return toPad + pattern&lt;br /&gt;
&lt;br /&gt;
	def __ANSI_X923_unpad (self, padded ):&lt;br /&gt;
		length = ord(padded[-1])&lt;br /&gt;
		#check if the bytes to be removed are all zero&lt;br /&gt;
		if padded.count(&#039;\x00&#039;,-length,-1) == length - 1:&lt;br /&gt;
			return padded[:-length]&lt;br /&gt;
		else:&lt;br /&gt;
			print &#039;error: padding pattern not recognized %s&#039; % padded.count(&#039;\x00&#039;,-length,-1)&lt;br /&gt;
			return unpadded&lt;br /&gt;
&lt;br /&gt;
	def __ISO_10126 (self, toPad):&lt;br /&gt;
		bytesToPad = self.blockSize - len(toPad)%self.blockSize&lt;br /&gt;
		pattern1 = &#039;&#039;.join(chr(random.randrange(256)) for x in range(bytesToPad-1))&lt;br /&gt;
		pattern2 = chr(bytesToPad)&lt;br /&gt;
		return toPad + pattern1 + pattern2&lt;br /&gt;
&lt;br /&gt;
	def __ISO_10126_unpad (self, padded):&lt;br /&gt;
		return padded[:-ord(padded[-1])]&lt;br /&gt;
&lt;br /&gt;
def main():&lt;br /&gt;
	usage = &amp;quot;usage: %prog [options] [texttopad]&amp;quot;&lt;br /&gt;
	parser = OptionParser(usage=usage)&lt;br /&gt;
	parser.add_option(&amp;quot;-b&amp;quot;, &amp;quot;--blocksize&amp;quot;, dest=&#039;blocksize&#039;, default=8, help=&amp;quot;specify the bloksize&amp;quot;, type=&#039;int&#039;)&lt;br /&gt;
	(options, args) = parser.parse_args()&lt;br /&gt;
	&lt;br /&gt;
	if len(args) &amp;gt; 1:&lt;br /&gt;
		parser.error(&amp;quot;Program takes maximum 1 argument&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	try:&lt;br /&gt;
		testbench(args[0].decode(&#039;string_escape&#039;),options.blocksize)&lt;br /&gt;
	except IndexError:&lt;br /&gt;
		testbench(blocksize=options.blocksize)&lt;br /&gt;
&lt;br /&gt;
def testbench(toPad=&#039;test&#039;,blocksize=8):&lt;br /&gt;
	testbench = (&#039;bitPadding&#039;,&#039;zerosPadding&#039;,&#039;PKCS7&#039;,&#039;ANSI_X923&#039;,&#039;ISO_10126&#039;)&lt;br /&gt;
	padder = Padding(blocksize)&lt;br /&gt;
	print &amp;quot;String to be padded: %r, with length %i\n&amp;quot; % (toPad,len(toPad))&lt;br /&gt;
	for test in testbench:&lt;br /&gt;
		padded = padder.pad(toPad,&#039;%s&#039;%test)&lt;br /&gt;
		print &amp;quot;padded: %r&amp;quot; % padded&lt;br /&gt;
		unpadded = padder.unpad(padded,&#039;%s&#039;%test)&lt;br /&gt;
		print &amp;quot;unpadded: %r&amp;quot; % unpadded&lt;br /&gt;
		if unpadded == toPad:&lt;br /&gt;
			print &amp;quot;%s test OK!\n&amp;quot; % test&lt;br /&gt;
		else:&lt;br /&gt;
			print &amp;quot;%s test not OK!\n&amp;quot; % test&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
	main()&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Download code: [{{#filelink: padding.py}} padding.py]&lt;/div&gt;</summary>
		<author><name>Newacct</name></author>
	</entry>
	<entry>
		<id>https://wiki.yobi.be/index.php?title=Mediawiki_RawFile&amp;diff=5973</id>
		<title>Mediawiki RawFile</title>
		<link rel="alternate" type="text/html" href="https://wiki.yobi.be/index.php?title=Mediawiki_RawFile&amp;diff=5973"/>
		<updated>2009-10-19T08:02:38Z</updated>

		<summary type="html">&lt;p&gt;Newacct: /* Hook to intercept the raw output */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Very short introduction==&lt;br /&gt;
Just have a look to the 2 [[Mediawiki_RawFile#Short_example|examples]] to see how to use the extension&lt;br /&gt;
&amp;lt;br&amp;gt;and to the [[Mediawiki_RawFile#Installation|Installation]] section to see how to install the extension in your [[MediaWiki]] server&lt;br /&gt;
==Introduction==&lt;br /&gt;
Originally the idea was to be able to download directly a portion of code as a file.&lt;br /&gt;
&amp;lt;br&amp;gt;I&#039;ve numerous code examples in my wiki and I wanted an easy way to download them, easier than a copy/paste!&lt;br /&gt;
&amp;lt;br&amp;gt;But from there it was rather easy to get something very close to [http://en.wikipedia.org/wiki/Literate_programming literate programming] just by allowing multiple blocks referring to the same file, which will be concatenated together at download time.&lt;br /&gt;
&lt;br /&gt;
* It must work with pre, nowiki, js, css, code, source, so let&#039;s make it general: take the tag that comes after the parser function we&#039;ll create and select data up to the closing tag.&lt;br /&gt;
* There are two distinct functionalities provided by the extension: &lt;br /&gt;
** the parser that will convert a magic word into a link to the download URL&lt;br /&gt;
** an extended ?action=raw that will strip the raw output to keep the desired code&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
There are 3 kinds of elements to add to the wiki language:&lt;br /&gt;
* anchors that will flag which code blocks belong to a specific file&lt;br /&gt;
** &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#fileAnchor: myscript.sh}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
** Not visible in the regular wiki display&lt;br /&gt;
* links that will allow to download the file&lt;br /&gt;
** &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#fileLink: myscript.sh}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
** or &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#fileLink: myscript.sh|Another Page}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
** The first syntax is used when the file is on the same page. It is transformed into new regular wikicode that will be eventually transformed to real URLs: &amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{fullurl:{{PAGENAME}}|action=raw&amp;amp;file=myscript.sh}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;http://wiki.yobi.be/index.php?title=Mediawiki_RawFile&amp;amp;action=raw&amp;amp;file=myscript.sh&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
** The second syntax indicates that the file is on another local wiki page. It is eventually transformed into the real URLs: &amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{fullurl:{{PAGENAME}}|action=raw&amp;amp;file=myscript.sh}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;http://wiki.yobi.be/index.php?title=Another_Page&amp;amp;action=raw&amp;amp;file=myscript.sh&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* a shortcut notation mixing both an anchor and download link, handy for regular use, when a single code block is used and when the download link can be at the same position as the anchor&lt;br /&gt;
** &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{#file: myscript.sh}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Short example===&lt;br /&gt;
The extension works with any block such as pre, nowiki, js, css, code, source,...&lt;br /&gt;
&amp;lt;br&amp;gt;This example is using the syntax highlighting &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt; tag provided by [http://www.mediawiki.org/wiki/Extension:SyntaxHighlight_GeSHi SyntaxHighlight extension] (using [http://qbnz.com/highlighter/ GeSHi Highlighter])&lt;br /&gt;
&amp;lt;br&amp;gt;If you didn&#039;t install that extension on your MediaWiki, you can try the example by using &amp;lt;nowiki&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/nowiki&amp;gt; instead of &amp;lt;nowiki&amp;gt;&amp;lt;source&amp;gt;&amp;lt;/nowiki&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Let&#039;s save the following code [{{#file: myscript.sh}} as myscript.sh]&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
echo &#039;Hello world!&#039;&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
will give:&lt;br /&gt;
----&lt;br /&gt;
Let&#039;s save the following code [{{#file: myscript.sh}} as myscript.sh]&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
echo &#039;Hello world!&#039;&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Complete example===&lt;br /&gt;
And a full example with anchors &amp;amp; link:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Let&#039;s start with the Bash usual header:&lt;br /&gt;
{{#fileanchor: myotherscript.sh}}&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Then we&#039;ll display a welcome message:&lt;br /&gt;
{{#fileanchor: myotherscript.sh}}&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
echo &#039;Welcome on earth!&#039;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
And we finally exit cleanly:&lt;br /&gt;
{{#fileanchor: myotherscript.sh}}&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
[{{#filelink: myotherscript.sh}} myotherscript.sh is now available for download below the code]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
will give:&lt;br /&gt;
----&lt;br /&gt;
Let&#039;s start with the Bash usual header:&lt;br /&gt;
{{#fileanchor: myotherscript.sh}}&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Then we&#039;ll display a welcome message:&lt;br /&gt;
{{#fileanchor: myotherscript.sh}}&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
echo &#039;Welcome on earth!&#039;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
And we finally exit cleanly:&lt;br /&gt;
{{#fileanchor: myotherscript.sh}}&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
exit 0&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
[{{#filelink: myotherscript.sh}} myotherscript.sh is now available for download below the code]&lt;br /&gt;
&lt;br /&gt;
==The code (the ultimate example)==&lt;br /&gt;
Which you can of course download just by following [{{#filelink: RawFile.php}} this link :-)]&lt;br /&gt;
&lt;br /&gt;
So let&#039;s explain a bit the code in a Literate Programming way...&lt;br /&gt;
===Hooks===&lt;br /&gt;
First some hooks for our functions...&lt;br /&gt;
&lt;br /&gt;
We will create:&lt;br /&gt;
* a [http://www.mediawiki.org/wiki/Manual:Parser_functions Parser Function] (see also [http://meta.wikimedia.org/wiki/Help:Parser_function here]), with help of&lt;br /&gt;
** [http://www.mediawiki.org/wiki/Manual:%24wgExtensionFunctions $wgExtensionFunctions] to define the setup function&lt;br /&gt;
** [http://www.mediawiki.org/wiki/Manual:Magic_words Magic Words]&lt;br /&gt;
** [http://www.mediawiki.org/wiki/Manual:Hooks/LanguageGetMagic LanguageGetMagic] hook to initialize the magic words&lt;br /&gt;
* a [http://www.mediawiki.org/wiki/Manual:Hooks/RawPageViewBeforeOutput RawPageViewBeforeOutput] hook to intercept the raw output&lt;br /&gt;
&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
if (defined(&#039;MEDIAWIKI&#039;)) {&lt;br /&gt;
&lt;br /&gt;
$wgExtensionFunctions[] = &#039;efRawFile_Setup&#039;;&lt;br /&gt;
$wgHooks[&#039;LanguageGetMagic&#039;][]       = &#039;efRawFile_Magic&#039;;&lt;br /&gt;
$wgHooks[&#039;RawPageViewBeforeOutput&#039;][] = &#039;fnRawFile_Strip&#039;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Setup function===&lt;br /&gt;
For the wiki parsing to create download links, file and fileLink are equally treated, while fileAnchor will be simply left out.&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
function efRawFile_Setup() {&lt;br /&gt;
    global $wgParser;&lt;br /&gt;
    $wgParser-&amp;gt;setFunctionHook( &#039;file&#039;, &#039;efRawFile_Render&#039; );&lt;br /&gt;
    $wgParser-&amp;gt;setFunctionHook( &#039;filelink&#039;, &#039;efRawFile_Render&#039; );&lt;br /&gt;
    $wgParser-&amp;gt;setFunctionHook( &#039;fileanchor&#039;, &#039;efRawFile_Empty&#039; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Hook to initialize the magic words===&lt;br /&gt;
We add the magic words here: the first array element indicates if it is case sensitive, in this case it is not case sensitive. We could add extra elements to create synonyms for our parser function.&lt;br /&gt;
&amp;lt;br&amp;gt;Unless we return true, other parser functions extensions will not get loaded.&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
function efRawFile_Magic( &amp;amp;$magicWords, $langCode ) {&lt;br /&gt;
    $magicWords[&#039;file&#039;] = array( 0, &#039;file&#039; );&lt;br /&gt;
    $magicWords[&#039;filelink&#039;] = array( 0, &#039;filelink&#039; );&lt;br /&gt;
    $magicWords[&#039;fileanchor&#039;] = array( 0, &#039;fileanchor&#039; );&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parser functions of the magic words===&lt;br /&gt;
The transformation rule to replace link shortcuts to actual links for download, handling an optional local wiki page title if present.&lt;br /&gt;
&amp;lt;br&amp;gt;The input parameters are wikitext with templates expanded, the output should be wikitext too&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;TODO&#039;&#039;&#039;: what error to send out if there is no filename given?&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;EDIT&#039;&#039;&#039;: It seems that [http://svn.wikimedia.org/viewvc/mediawiki?view=rev&amp;amp;revision=27667 commit 27667] (1.11 -&amp;gt; 1.12) changed the default parser, which breaks the recursive parsing. Thanks to Tim Starling for helping me to get around the problem!&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
function efRawFile_Render( &amp;amp;$parser, $filename = &#039;&#039;, $titleText = &#039;&#039;) {&lt;br /&gt;
    if( $titleText == &#039;&#039; )&lt;br /&gt;
        $title = $parser-&amp;gt;mTitle;&lt;br /&gt;
    else&lt;br /&gt;
        $title = Title::newFromText( $titleText );&lt;br /&gt;
    return $title-&amp;gt;getFullURL( &#039;action=raw&amp;amp;file=&#039;.urlencode( $filename ) );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
And the other one, just removing the anchors from the rendered wiki page.&lt;br /&gt;
&amp;lt;br&amp;gt;Curiously enough if the function doesn&#039;t exist at all the effect is exactly the same, MW doesn&#039;t throw any error.&lt;br /&gt;
&amp;lt;br&amp;gt;But let&#039;s keep things clean...&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
function efRawFile_Empty( &amp;amp;$parser, $filename = &#039;&#039;) {&lt;br /&gt;
    return &#039;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Hook to intercept the raw output===&lt;br /&gt;
This part of the code doesn&#039;t look that nice because we&#039;ve to parse the raw wiki page ourselves to retrieve the code sections we want.&lt;br /&gt;
&lt;br /&gt;
First let&#039;s see if &amp;lt;code&amp;gt;?action=raw&amp;lt;/code&amp;gt; was used in the context of this extension: in that case we receive the filename as GET parameter, otherwise we simply return from our extension with return value=true which means we authorize the raw display (originally the hook was created to add an authentication point)&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
function fnRawFile_Strip(&amp;amp;$rawPage, &amp;amp;$text) {&lt;br /&gt;
    if (!isset($_GET[&#039;file&#039;]))&lt;br /&gt;
        return true;&lt;br /&gt;
    $filename=$_GET[&#039;file&#039;];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
By default the downloadable file will still be handled by the ob_gzhandler session made by Mediawiki. To avoid output buffering and gzipping, one can uncomment the following line:&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
    // Uncomment the following line to avoid output buffering and gzipping:&lt;br /&gt;
    // wfResetOutputBuffers();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Raw action already set the headers with some client cache pragmas and is supposed to be displayed in the browser but in our case we want to make this &amp;quot;page&amp;quot; a downloadable file so we overwrite the headers which were defined and we add a few more, to ensure there is no caching on the client (it&#039;s very hard for the client to force a refresh on a file download, contrary to a web page) and to provide the adequate filename.&lt;br /&gt;
&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
    header(&amp;quot;Content-disposition: attachment;filename={$filename}&amp;quot;);&lt;br /&gt;
    header(&amp;quot;Content-type: application/octetstream&amp;quot;); &lt;br /&gt;
    header(&amp;quot;Content-Transfer-Encoding: binary&amp;quot;); &lt;br /&gt;
    header(&amp;quot;Expires: 0&amp;quot;);&lt;br /&gt;
    header(&amp;quot;Pragma: no-cache&amp;quot;); &lt;br /&gt;
    header(&amp;quot;Cache-Control: no-store&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Then we&#039;ll strip the output, first we&#039;ve to locate the anchors but there are anchors that could be protected in literal blocks like nowiki.&lt;br /&gt;
&amp;lt;br&amp;gt;So we&#039;ll mask the literal blocks before searching for the anchors (we mask with the same string length because we&#039;ll retrieve an offset that we will use on the initial string and offsets must match)&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;TODO&#039;&#039;&#039;: should we care also of source, js, css, pre,... blocks? &lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
    $maskedtext=preg_replace(&#039;/&amp;lt;nowiki&amp;gt;.*?&amp;lt;\/nowiki&amp;gt;/e&#039;, &lt;br /&gt;
        &#039;ereg_replace(&amp;quot;.&amp;quot;,&amp;quot;X&amp;quot;,$0)&#039;,&lt;br /&gt;
        $text);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Now we can search for the anchors (or the short version, in which case we only keep the first hit, no multiple blocks support)&lt;br /&gt;
&amp;lt;br&amp;gt;And we free the memory used for the masked version&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;TODO&#039;&#039;&#039;: instead of cowardly returning if we don&#039;t find our anchors, we should cancel the headers and return a proper error page&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
    if (preg_match_all(&#039;/{{#fileanchor: *&#039;.$filename.&#039; *}}/i&#039;, $maskedtext, $matches, PREG_OFFSET_CAPTURE))&lt;br /&gt;
        $offsets=$matches[0];&lt;br /&gt;
    else if (preg_match_all(&#039;/{{#file: *&#039;.$filename.&#039; *}}/i&#039;, $maskedtext, $matches, PREG_OFFSET_CAPTURE))&lt;br /&gt;
        $offsets=array($matches[0][0]);&lt;br /&gt;
    else&lt;br /&gt;
        // We didn&#039;t find our anchor, let&#039;s output all the raw...&lt;br /&gt;
        return true;&lt;br /&gt;
    unset($maskedtext);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
$text is both input &amp;amp; output so we copy it and start with an empty output.&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
    $textorig=$text;&lt;br /&gt;
    $text=&#039;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
For each anchor found we&#039;ve to isolate the content of the next block.&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
    foreach ($offsets as $offset) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Let&#039;s remove the text up to the tag following the anchor&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;TODO&#039;&#039;&#039;: the next tag could be a &amp;lt; br &amp;gt;, which we should skip&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
        $out = substr($textorig, $offset[1]);&lt;br /&gt;
        $out = substr($out, strpos($out, &#039;&amp;lt;&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
What type of tag do we have?&lt;br /&gt;
&amp;lt;br&amp;gt;Note that we&#039;re looking to the word directly following &#039;&amp;lt;&#039; up to &#039;&amp;gt;&#039; or a space, e.g. if there are arguments to the tag.&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;TODO&#039;&#039;&#039;: once again, better handling of errors than just returning.&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
        if (!preg_match(&#039;/^&amp;lt;([^&amp;gt; ]+)/&#039;, $out, $matches))&lt;br /&gt;
            return true;&lt;br /&gt;
        $key = $matches[1];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
OK, let&#039;s extract the text up to the closing tag&lt;br /&gt;
&amp;lt;br&amp;gt;We skip the first carriage return after the opening tag, if any&lt;br /&gt;
&amp;lt;br&amp;gt;We look for the closing tag and we take what&#039;s in between.&lt;br /&gt;
&amp;lt;br&amp;gt;&#039;&#039;&#039;TODO&#039;&#039;&#039;: once again, better handling of errors than just returning.&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
        $begin = strpos($out, &#039;&amp;gt;&#039;)+1;&lt;br /&gt;
        if (ord(substr($out,$begin,1))==10)&lt;br /&gt;
            $begin++;&lt;br /&gt;
        if (preg_match_all(&#039;/&amp;lt;\/&#039;.$key.&#039;&amp;gt;/&#039;, $out, $matches, PREG_OFFSET_CAPTURE))&lt;br /&gt;
            $text .= substr($out, $begin, $matches[0][0][1]-$begin);&lt;br /&gt;
        else&lt;br /&gt;
            // error, we could not find end of bloc&lt;br /&gt;
            $text .= substr($out, $begin);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;No need to deal with a Content-Length header because Mediawiki will do it for us, moreover more properly than we could if the output is sent gzipped, which is the default.&lt;br /&gt;
&amp;lt;br&amp;gt;So that&#039;s it, $text contains our file!&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Credits===&lt;br /&gt;
There is an official way to register the extension in a Mediawiki installation, so that it will be visible on the [[Special:Version]] page.&lt;br /&gt;
&amp;lt;br&amp;gt;Let&#039;s say the extension is in the category of parser hooks even if there is also a hook on Raw action.&lt;br /&gt;
{{#fileanchor: RawFile.php}}&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
$wgExtensionCredits[&#039;parserhook&#039;][] = array(&#039;name&#039; =&amp;gt; &#039;RawFile&#039;,&lt;br /&gt;
                           &#039;version&#039; =&amp;gt; &#039;0.3&#039;,&lt;br /&gt;
                           &#039;author&#039; =&amp;gt; &#039;Philippe Teuwen&#039;,&lt;br /&gt;
                           &#039;url&#039; =&amp;gt; &#039;http://www.mediawiki.org/wiki/Extension:RawFile&#039;,&lt;br /&gt;
//                         &#039;url&#039; =&amp;gt; &#039;http://wiki.yobi.be/wiki/Mediawiki_RawFile&#039;,&lt;br /&gt;
                           &#039;description&#039; =&amp;gt; &#039;Downloads a RAW copy of &amp;lt;nowiki&amp;gt;&amp;lt;tag&amp;gt;data&amp;lt;/tag&amp;gt;&amp;lt;/nowiki&amp;gt; in a file&amp;lt;br&amp;gt;&#039;.&lt;br /&gt;
                                            &#039;Useful e.g. to download a script or a patch&amp;lt;br&amp;gt;&#039;.&lt;br /&gt;
                                            &#039;It also allows what is called [http://en.wikipedia.org/wiki/Literate_programming Literate Programming]&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
And finally registration of the extension at the Mediawiki website according to the [http://www.mediawiki.org/wiki/Manual:Extensions Extensions Manual].&lt;br /&gt;
&lt;br /&gt;
So this extension has now [http://www.mediawiki.org/wiki/Extension:RawFile its own page on the official Mediawiki site].&lt;br /&gt;
&lt;br /&gt;
==Installation==&lt;br /&gt;
Download [{{#filelink: RawFile.php}} RawFile.php] and save it under the MediaWiki directory as extensions/RawFile/RawFile.php&lt;br /&gt;
&lt;br /&gt;
Add at the end of LocalSettings.php:&lt;br /&gt;
&amp;lt;source lang=php&amp;gt;&lt;br /&gt;
require_once(&amp;quot;$IP/extensions/RawFile/RawFile.php&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
==Status==&lt;br /&gt;
If you use the extension properly the code is fully functional but it&#039;s rather raw on error handling.&lt;br /&gt;
==ChangeLog==&lt;br /&gt;
&#039;&#039;&#039;0.3&#039;&#039;&#039;&lt;br /&gt;
* Added optional parameter to &amp;lt;code&amp;gt;#fileLink&amp;lt;/code&amp;gt; to indicate that the file is on another local wiki page&lt;br /&gt;
&#039;&#039;&#039;0.2&#039;&#039;&#039;&lt;br /&gt;
* Fix problem with Content-Length mismatch when transport is gzipped (default for Mediawiki if client supports it)&lt;br /&gt;
&#039;&#039;&#039;0.1&#039;&#039;&#039;&lt;br /&gt;
* Initial version&lt;br /&gt;
==Known bugs==&lt;br /&gt;
Jani Uusitalo reported the following issue: &lt;br /&gt;
&amp;lt;br&amp;gt;For some reason, if you use Epiphany&#039;s &#039;Save as&#039; instead of a direct left-click, the downloaded file is a single byte. In Firefox the links work just fine, so this is probably an Epiphany bug. Uncommenting the &amp;lt;code&amp;gt;// wfResetOutputBuffers();&amp;lt;/code&amp;gt; line didn&#039;t help. If anyone knows a workaround to help Epiphany users without breaking the support of the other browsers, please speak up :-)&lt;br /&gt;
&lt;br /&gt;
==Questions and feedback==&lt;br /&gt;
If you&#039;ve any trouble, questions or suggestions, you can [[User:PhilippeTeuwen|contact me]].&lt;/div&gt;</summary>
		<author><name>Newacct</name></author>
	</entry>
</feed>