<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://www.briskbee.com/feed.xml" rel="self" type="application/atom+xml" /><link href="http://www.briskbee.com/" rel="alternate" type="text/html" /><updated>2026-06-03T19:24:35+00:00</updated><id>http://www.briskbee.com/feed.xml</id><title type="html">Brisk Bee</title><subtitle>Rants on mobile development</subtitle><entry><title type="html">Using PBKDF2 on multiple platforms</title><link href="http://www.briskbee.com/2018/06/using-pbkdf2-on-multiple-platforms.html" rel="alternate" type="text/html" title="Using PBKDF2 on multiple platforms" /><published>2018-06-14T00:00:00+00:00</published><updated>2018-06-14T00:00:00+00:00</updated><id>http://www.briskbee.com/2018/06/using-pbkdf2-on-multiple-platforms</id><content type="html" xml:base="http://www.briskbee.com/2018/06/using-pbkdf2-on-multiple-platforms.html"><![CDATA[<p><a href="https://en.wikipedia.org/wiki/PBKDF2">PBKDF2</a> is a popular choice when it comes to password hashing. I recently looked into PBKDF2 and wanted to use it across multiple platforms and spent some time trying to figure out how to implement it and how to make sure that hashes were the same no matter which platform they were generated on. The programming languages I used was Swift, Java and JavaScript (Node).</p>

<p>I have created a <a href="https://github.com/peterhaldbaek/pbkdf2">GitHub repo</a> where all code referenced here can be found.</p>

<h1 id="swift">Swift</h1>

<p>First language up was Swift. Since iOS is my main playground these days this was where I started out. PBKDF2 is more or less supported out of the box using CommonCrypto. We need to add a bridging header to the project and import CommonCrypto in the header.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#import &lt;CommonCrypto/CommonCrypto.h&gt;</span>
</code></pre></div></div>

<p>Now CommonCrypto is available in the project and we can reference it as any other framework. The code for creating the PBKDF2 hash looks like this</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">pbkdf2</span><span class="p">(</span><span class="nv">hash</span><span class="p">:</span> <span class="kt">CCPBKDFAlgorithm</span><span class="p">,</span> <span class="nv">password</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">salt</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">keyByteCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">rounds</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">passwordData</span> <span class="o">=</span> <span class="n">password</span><span class="o">.</span><span class="nf">data</span><span class="p">(</span><span class="nv">using</span><span class="p">:</span> <span class="o">.</span><span class="n">utf8</span><span class="p">)</span><span class="o">!</span>
    <span class="k">let</span> <span class="nv">saltData</span> <span class="o">=</span> <span class="n">salt</span><span class="o">.</span><span class="nf">data</span><span class="p">(</span><span class="nv">using</span><span class="p">:</span> <span class="o">.</span><span class="n">utf8</span><span class="p">)</span><span class="o">!</span>
    <span class="k">var</span> <span class="nv">derivedKeyData</span> <span class="o">=</span> <span class="kt">Data</span><span class="p">(</span><span class="nv">repeating</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">count</span><span class="p">:</span> <span class="n">keyByteCount</span><span class="p">)</span>
    
    <span class="k">var</span> <span class="nv">localDerivedKeyData</span> <span class="o">=</span> <span class="n">derivedKeyData</span>
    
    <span class="k">let</span> <span class="nv">derivationStatus</span> <span class="o">=</span> <span class="n">derivedKeyData</span><span class="o">.</span><span class="n">withUnsafeMutableBytes</span> <span class="p">{</span> <span class="n">derivedKeyBytes</span> <span class="k">in</span>
        <span class="n">saltData</span><span class="o">.</span><span class="n">withUnsafeBytes</span> <span class="p">{</span> <span class="n">saltBytes</span> <span class="k">in</span>
            
            <span class="kt">CCKeyDerivationPBKDF</span><span class="p">(</span>
                <span class="kt">CCPBKDFAlgorithm</span><span class="p">(</span><span class="n">kCCPBKDF2</span><span class="p">),</span>
                <span class="n">password</span><span class="p">,</span> <span class="n">passwordData</span><span class="o">.</span><span class="n">count</span><span class="p">,</span>
                <span class="n">saltBytes</span><span class="p">,</span> <span class="n">saltData</span><span class="o">.</span><span class="n">count</span><span class="p">,</span>
                <span class="n">hash</span><span class="p">,</span>
                <span class="kt">UInt32</span><span class="p">(</span><span class="n">rounds</span><span class="p">),</span>
                <span class="n">derivedKeyBytes</span><span class="p">,</span> <span class="n">localDerivedKeyData</span><span class="o">.</span><span class="n">count</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">derivationStatus</span> <span class="o">!=</span> <span class="n">kCCSuccess</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"Error: </span><span class="se">\(</span><span class="n">derivationStatus</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="kc">nil</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="k">return</span> <span class="nf">toHex</span><span class="p">(</span><span class="n">derivedKeyData</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// Converts data to a hexadecimal string</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">toHex</span><span class="p">(</span><span class="n">_</span> <span class="nv">data</span><span class="p">:</span> <span class="kt">Data</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">data</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="kt">String</span><span class="p">(</span><span class="nv">format</span><span class="p">:</span> <span class="s">"%02x"</span><span class="p">,</span> <span class="nv">$0</span><span class="p">)</span> <span class="p">}</span><span class="o">.</span><span class="nf">joined</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I also added some helper functions for easily creating hashes based on which SHA algorithm I wanted to use.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">pbkdf2sha1</span><span class="p">(</span><span class="nv">password</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">salt</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">keyByteCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">rounds</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nf">pbkdf2</span><span class="p">(</span><span class="nv">hash</span><span class="p">:</span> <span class="kt">CCPBKDFAlgorithm</span><span class="p">(</span><span class="n">kCCPRFHmacAlgSHA1</span><span class="p">),</span> <span class="nv">password</span><span class="p">:</span> <span class="n">password</span><span class="p">,</span> <span class="nv">salt</span><span class="p">:</span> <span class="n">salt</span><span class="p">,</span> <span class="nv">keyByteCount</span><span class="p">:</span> <span class="n">keyByteCount</span><span class="p">,</span> <span class="nv">rounds</span><span class="p">:</span> <span class="n">rounds</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">pbkdf2sha256</span><span class="p">(</span><span class="nv">password</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">salt</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">keyByteCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">rounds</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nf">pbkdf2</span><span class="p">(</span><span class="nv">hash</span><span class="p">:</span> <span class="kt">CCPBKDFAlgorithm</span><span class="p">(</span><span class="n">kCCPRFHmacAlgSHA256</span><span class="p">),</span> <span class="nv">password</span><span class="p">:</span> <span class="n">password</span><span class="p">,</span> <span class="nv">salt</span><span class="p">:</span> <span class="n">salt</span><span class="p">,</span> <span class="nv">keyByteCount</span><span class="p">:</span> <span class="n">keyByteCount</span><span class="p">,</span> <span class="nv">rounds</span><span class="p">:</span> <span class="n">rounds</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">pbkdf2sha512</span><span class="p">(</span><span class="nv">password</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">salt</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">keyByteCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">rounds</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nf">pbkdf2</span><span class="p">(</span><span class="nv">hash</span><span class="p">:</span> <span class="kt">CCPBKDFAlgorithm</span><span class="p">(</span><span class="n">kCCPRFHmacAlgSHA512</span><span class="p">),</span> <span class="nv">password</span><span class="p">:</span> <span class="n">password</span><span class="p">,</span> <span class="nv">salt</span><span class="p">:</span> <span class="n">salt</span><span class="p">,</span> <span class="nv">keyByteCount</span><span class="p">:</span> <span class="n">keyByteCount</span><span class="p">,</span> <span class="nv">rounds</span><span class="p">:</span> <span class="n">rounds</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Theoretically using SHA256 or SHA512 should be preferred but in reality it should not make a big difference since the security of PBKDF2 also lies in stretching of the key and not only the SHA algorithm used.</p>

<p>The result of testing the function looks like this</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">pbkdf2sha1</span><span class="p">(</span><span class="nv">password</span><span class="p">:</span> <span class="s">"password"</span><span class="p">,</span> <span class="nv">salt</span><span class="p">:</span> <span class="s">"salt"</span><span class="p">,</span> <span class="nv">keyByteCount</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span> <span class="nv">rounds</span><span class="p">:</span> <span class="mi">5000</span><span class="p">)</span>
<span class="c1">// =&gt; edf738254821c55da61e6afa20efd0c657cb941c</span>
</code></pre></div></div>

<h1 id="node">Node</h1>

<p>Now lets see if we can produce similar results using Node.</p>

<p>Hashing using PBKDF2 is part of Node so no need to use other modules. I have seen modules on NPM offering PBKDF2 hashing but unless there are very specific reasons for using these modules just use the methods provided by Node.</p>

<p>PBKDF2 hashing resides in the crypto module so start by requiring (or importing) it.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">crypto</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">crypto</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>

<p>After that it is just a matter of calling the pbkdf2 function (or its synchronous cousin pbkdf2Sync).</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">crypto</span><span class="p">.</span><span class="nx">pbkdf2Sync</span><span class="p">(</span><span class="dl">"</span><span class="s2">password</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">salt</span><span class="dl">"</span><span class="p">,</span> <span class="mi">5000</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="dl">'</span><span class="s1">sha1</span><span class="dl">'</span><span class="p">).</span><span class="nx">toString</span><span class="p">(</span><span class="dl">'</span><span class="s1">hex</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">// =&gt; edf738254821c55da61e6afa20efd0c657cb941c</span>
</code></pre></div></div>

<p>As you can see the hash matches the one produced by Swift so it looks like we are heading in the right direction!</p>

<h1 id="java">Java</h1>

<p>Finally we need to be able to produce the same hash using Java.</p>

<p>Java also supports PBKDF2 out of the box using the javax.crypto package. The examples used here were meant to run on the server so I honestly do not know if it will run on an Android device since the Dalvik VM is a scaled down version of what runs in a standard JVM. It might work, it might not.</p>

<p>Start out by importing the SecretKeyFactory and the PBEKeySpec.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">javax.crypto.SecretKeyFactory</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.crypto.spec.PBEKeySpec</span><span class="o">;</span>
</code></pre></div></div>

<p>After that it is just a matter of using the snippet below.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">pbkdf2</span><span class="o">(</span><span class="nc">String</span> <span class="n">password</span><span class="o">,</span> <span class="nc">String</span> <span class="n">salt</span><span class="o">,</span> <span class="kt">int</span> <span class="n">iterations</span><span class="o">,</span> <span class="kt">int</span> <span class="n">keyLength</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">NoSuchAlgorithmException</span><span class="o">,</span> <span class="nc">InvalidKeySpecException</span> <span class="o">{</span>
    <span class="kt">char</span><span class="o">[]</span> <span class="n">chars</span> <span class="o">=</span> <span class="n">password</span><span class="o">.</span><span class="na">toCharArray</span><span class="o">();</span>

    <span class="nc">PBEKeySpec</span> <span class="n">spec</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PBEKeySpec</span><span class="o">(</span><span class="n">chars</span><span class="o">,</span> <span class="n">salt</span><span class="o">.</span><span class="na">getBytes</span><span class="o">(),</span> <span class="n">iterations</span><span class="o">,</span> <span class="n">keyLength</span><span class="o">);</span>
    <span class="nc">SecretKeyFactory</span> <span class="n">skf</span> <span class="o">=</span> <span class="nc">SecretKeyFactory</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(</span><span class="s">"PBKDF2WithHmacSHA1"</span><span class="o">);</span>
    <span class="kt">byte</span><span class="o">[]</span> <span class="n">hash</span> <span class="o">=</span> <span class="n">skf</span><span class="o">.</span><span class="na">generateSecret</span><span class="o">(</span><span class="n">spec</span><span class="o">).</span><span class="na">getEncoded</span><span class="o">();</span>
    <span class="k">return</span> <span class="nf">toHex</span><span class="o">(</span><span class="n">hash</span><span class="o">);</span>
<span class="o">}</span>

<span class="c1">// Converts byte array to a hexadecimal string</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">toHex</span><span class="o">(</span><span class="kt">byte</span><span class="o">[]</span> <span class="n">array</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">StringBuffer</span> <span class="n">sb</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuffer</span><span class="o">();</span>
    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">array</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
        <span class="n">sb</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="nc">Integer</span><span class="o">.</span><span class="na">toString</span><span class="o">((</span><span class="n">array</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="o">)</span> <span class="o">+</span> <span class="mh">0x100</span><span class="o">,</span> <span class="mi">16</span><span class="o">).</span><span class="na">substring</span><span class="o">(</span><span class="mi">1</span><span class="o">));</span>
    <span class="o">}</span>
    <span class="k">return</span> <span class="n">sb</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Testing it reveals this surprising result</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pbkdf2</span><span class="o">(</span><span class="s">"password"</span><span class="o">,</span> <span class="s">"salt"</span><span class="o">,</span> <span class="mi">5000</span><span class="o">,</span> <span class="mi">20</span><span class="o">);</span>
<span class="c1">// =&gt; edf7</span>
</code></pre></div></div>

<p>Hmmm. Not quite what we expected. After some fiddling with the method it turns out we have to multiply the keyLength with 8 to provide the same results as with Swift and Node.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="nc">String</span> <span class="nf">pbkdf2</span><span class="o">(</span><span class="nc">String</span> <span class="n">password</span><span class="o">,</span> <span class="nc">String</span> <span class="n">salt</span><span class="o">,</span> <span class="kt">int</span> <span class="n">iterations</span><span class="o">,</span> <span class="kt">int</span> <span class="n">keyLength</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">NoSuchAlgorithmException</span><span class="o">,</span> <span class="nc">InvalidKeySpecException</span> <span class="o">{</span>
    <span class="kt">char</span><span class="o">[]</span> <span class="n">chars</span> <span class="o">=</span> <span class="n">password</span><span class="o">.</span><span class="na">toCharArray</span><span class="o">();</span>

    <span class="nc">PBEKeySpec</span> <span class="n">spec</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PBEKeySpec</span><span class="o">(</span><span class="n">chars</span><span class="o">,</span> <span class="n">salt</span><span class="o">.</span><span class="na">getBytes</span><span class="o">(),</span> <span class="n">iterations</span><span class="o">,</span> <span class="n">keyLength</span><span class="o">*</span><span class="mi">8</span><span class="o">);</span>
    <span class="nc">SecretKeyFactory</span> <span class="n">skf</span> <span class="o">=</span> <span class="nc">SecretKeyFactory</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(</span><span class="s">"PBKDF2WithHmacSHA1"</span><span class="o">);</span>
    <span class="kt">byte</span><span class="o">[]</span> <span class="n">hash</span> <span class="o">=</span> <span class="n">skf</span><span class="o">.</span><span class="na">generateSecret</span><span class="o">(</span><span class="n">spec</span><span class="o">).</span><span class="na">getEncoded</span><span class="o">();</span>
    <span class="k">return</span> <span class="nf">toHex</span><span class="o">(</span><span class="n">hash</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<p>This produces the following result which aligns nicely with the results from Swift and Node.</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pbkdf2</span><span class="o">(</span><span class="s">"password"</span><span class="o">,</span> <span class="s">"salt"</span><span class="o">,</span> <span class="mi">5000</span><span class="o">,</span> <span class="mi">20</span><span class="o">);</span>
<span class="c1">// =&gt; edf738254821c55da61e6afa20efd0c657cb941c</span>
</code></pre></div></div>

<p>That’s it. Again, everything is available at <a href="https://github.com/peterhaldbaek/pbkdf2">GitHub</a>, enjoy!</p>]]></content><author><name>Peter Haldbæk</name></author><summary type="html"><![CDATA[PBKDF2 is a popular choice when it comes to password hashing. I recently looked into PBKDF2 and wanted to use it across multiple platforms and spent some time trying to figure out how to implement it and how to make sure that hashes were the same no matter which platform they were generated on. The programming languages I used was Swift, Java and JavaScript (Node).]]></summary></entry><entry><title type="html">Hello Jekyll</title><link href="http://www.briskbee.com/2018/04/hello-jekyll.html" rel="alternate" type="text/html" title="Hello Jekyll" /><published>2018-04-11T00:00:00+00:00</published><updated>2018-04-11T00:00:00+00:00</updated><id>http://www.briskbee.com/2018/04/hello-jekyll</id><content type="html" xml:base="http://www.briskbee.com/2018/04/hello-jekyll.html"><![CDATA[<p>Back from a long blogging hiatus I decided to call it quits on DocPad and move the blog to a simpler platform. Although I did like DocPad it seemed like it was losing support and in the end it was just too cumbersome to keep track on Node versions, modules and what not.</p>

<p>So now I’m taking a dive with Jekyll and GitHub Pages. Why? Posting is just a whole lot easier when all you need to do is write something directly on GitHub. No deployment steps or anything, just plain markdown. And having support for tables (through kramdown) was a nice bonus as well. So time to start blogging on my latest endevaours into iOS and Swift!</p>]]></content><author><name>Peter Haldbæk</name></author><summary type="html"><![CDATA[Back from a long blogging hiatus I decided to call it quits on DocPad and move the blog to a simpler platform. Although I did like DocPad it seemed like it was losing support and in the end it was just too cumbersome to keep track on Node versions, modules and what not.]]></summary></entry><entry><title type="html">Find Antenna for iOS gets huge update</title><link href="http://www.briskbee.com/2014/03/find-antenna-for-ios-gets-huge-update.html" rel="alternate" type="text/html" title="Find Antenna for iOS gets huge update" /><published>2014-03-26T00:00:00+00:00</published><updated>2014-03-26T00:00:00+00:00</updated><id>http://www.briskbee.com/2014/03/find-antenna-for-ios-gets-huge-update</id><content type="html" xml:base="http://www.briskbee.com/2014/03/find-antenna-for-ios-gets-huge-update.html"><![CDATA[<p>Months of waiting is finally over. The iOS version of Find Antenna finally supports the same countries as the Android version which means iPhone users in the United Kingdom, Germany and Sweden can now enjoy the app just as users in Denmark have been able to for some months. The update was also used to adjust some of the Danish transmitters since Boxer has decided to upgrade the transmitters at Borups Alle and Lynetten.</p>

<p>When the app starts it will automatically show you the transmitters of the country with the best signal. You can change country by clicking the menu icon at the top left and simply select the country you are interested in. It’s as easy as that!</p>

<p><img src="/assets/img/iphone5.png" alt="" /></p>

<p><a href="http://itunes.com/apps/findantenna"><img src="/assets/img/appstore.png" alt="" /></a></p>

<p>The app can be downloaded from the <a href="http://itunes.com/apps/findantenna">Apple App Store</a>.</p>]]></content><author><name>Peter Haldbæk</name></author><category term="iphone" /><category term="findantenna" /><summary type="html"><![CDATA[Months of waiting is finally over. The iOS version of Find Antenna finally supports the same countries as the Android version which means iPhone users in the United Kingdom, Germany and Sweden can now enjoy the app just as users in Denmark have been able to for some months. The update was also used to adjust some of the Danish transmitters since Boxer has decided to upgrade the transmitters at Borups Alle and Lynetten.]]></summary></entry><entry><title type="html">Secret menu for Huawei u8860</title><link href="http://www.briskbee.com/2014/03/secret-menu-huawei-u8860.html" rel="alternate" type="text/html" title="Secret menu for Huawei u8860" /><published>2014-03-11T00:00:00+00:00</published><updated>2014-03-11T00:00:00+00:00</updated><id>http://www.briskbee.com/2014/03/secret-menu-huawei-u8860</id><content type="html" xml:base="http://www.briskbee.com/2014/03/secret-menu-huawei-u8860.html"><![CDATA[<p>I have an old <a href="http://en.wikipedia.org/wiki/Huawei_u8860">Huawei 8860</a> Android phone which keeps running out of internal memory space. I constantly uninstall apps to make room not only for new apps but also just to be able to update my existing apps despite having more than 2GBs available according to the Storage settings. It turns out (I assume) that this behavior is caused by logs hiding in the <code>/data/log</code> folder. Too bad it is not possible to delete the files unless the device is rooted which I have not done (yet). I am still hoping to figure something out and recently found out that there is a secret system menu if you dial <code>\*#\*#2846579#\*#\*</code> in the stock phone app. It did not help me with my problem though.</p>

<h2 id="update-22042014">Update 22.04.2014</h2>
<p>After repeated searches for answers on the web about my internal memory issues with my Huawei came up fruitless I went ahead and rooted the device. Turns out there is no hidden <code>/data/log</code> folder (or any other folder containing garbage for that matter). I did find out that cleaning the <code>/data/tombstones</code> folder would give me (tiny) space improvements but overall the findings were very disapointing.</p>

<p>My next suspicion was looking a little bit closer at the storage required by the mandatory Google apps. It did seem like they were taking up a lot of space and since they cannot be moved to your SD card they are just hogging up internal memory. I figured a factory reset of my phone would reveal more. After the factory reset (but before updating all the mandatory apps) my phone had more than 600MB of internal memory (the phone is shipped with 1GB internal memory so I assume the rest is used for the OS). I updated all the mandatory Google apps and internal memory went down to 350MB! Experience has shown me that when the phone comes close to 100MB of free internal memory it starts getting cranky and <em>really</em> slow so this leaves me with 250MB of free space. Not really encouraging since all the mandatory apps like Facebook, Twitter, Google+, Gmail, Chrome, Hangouts and Maps caches data in internal memory so it is only a matter of (short) time before I am back to square one. Did I mention that the apps are mandatory despite I am not using apps like Google+ or Hangouts and possibly others as well?</p>

<p>I understand that older phones over time are considered inferior to newer models but I do not expect older phones to become useless all of a sudden as is the case right now. I believe there is a huge market for budget, low-spec smartphones (especially in 3rd World countries) but if the OS keeps requiring us to install bigger and bigger mandatory apps from Google I think they are making a mistake.</p>]]></content><author><name>Peter Haldbæk</name></author><category term="huawei" /><category term="android" /><summary type="html"><![CDATA[I have an old Huawei 8860 Android phone which keeps running out of internal memory space. I constantly uninstall apps to make room not only for new apps but also just to be able to update my existing apps despite having more than 2GBs available according to the Storage settings. It turns out (I assume) that this behavior is caused by logs hiding in the /data/log folder. Too bad it is not possible to delete the files unless the device is rooted which I have not done (yet). I am still hoping to figure something out and recently found out that there is a secret system menu if you dial \*#\*#2846579#\*#\* in the stock phone app. It did not help me with my problem though.]]></summary></entry><entry><title type="html">Find Antenna welcomes UK</title><link href="http://www.briskbee.com/2014/01/find-antenna-welcomes-uk.html" rel="alternate" type="text/html" title="Find Antenna welcomes UK" /><published>2014-01-25T00:00:00+00:00</published><updated>2014-01-25T00:00:00+00:00</updated><id>http://www.briskbee.com/2014/01/find-antenna-welcomes-uk</id><content type="html" xml:base="http://www.briskbee.com/2014/01/find-antenna-welcomes-uk.html"><![CDATA[<p>From the Baltasound transmitter on the Shetland Islands in the North to the tiny St Brelades Bay transmitter on Jersey in the South, from the Lowestoft transmitter of Suffolk in the East to the Belcoo transmitter in Northern Ireland to the West. <a href="https://play.google.com/store/apps/details?id=com.briskbee.master">Find Antenna for Android</a> now supports the entire United Kingdom.</p>

<p><a href="http://www.flickr.com/photos/ramnaganat/7981888270/"><img src="/assets/img/stonehenge.jpg" alt="" /></a></p>
<p class="license"><a href="http://creativecommons.org/licenses/by/2.0/deed.en_GB">CC</a> photo by <a href="http://www.flickr.com/photos/ramnaganat/">Natesh Ramasamy</a></p>

<p>Users of Find Antenna for iPhone must wait a little longer to enjoy this, but do not worry, it is coming to the iPhone as well in the near future!</p>]]></content><author><name>Peter Haldbæk</name></author><category term="android" /><category term="findantenna" /><summary type="html"><![CDATA[From the Baltasound transmitter on the Shetland Islands in the North to the tiny St Brelades Bay transmitter on Jersey in the South, from the Lowestoft transmitter of Suffolk in the East to the Belcoo transmitter in Northern Ireland to the West. Find Antenna for Android now supports the entire United Kingdom.]]></summary></entry><entry><title type="html">Hello DocPad</title><link href="http://www.briskbee.com/2014/01/hello-docpad.html" rel="alternate" type="text/html" title="Hello DocPad" /><published>2014-01-16T00:00:00+00:00</published><updated>2014-01-16T00:00:00+00:00</updated><id>http://www.briskbee.com/2014/01/hello-docpad</id><content type="html" xml:base="http://www.briskbee.com/2014/01/hello-docpad.html"><![CDATA[<p>Today I relaunched my blog. I wanted to skip the <a href="http://blogger.com">Blogger</a> platform for something more flexible for a long time and now I have finally made the switch. I examined a lot of Node blogging platforms and decided to go with <a href="http://docpad.org">DocPad</a>. I looked at other alternatives such as <a href="https://github.com/creationix/wheat">Wheat</a> and <a href="https://github.com/flatiron/blacksmith">Blacksmith</a> but the simplicity of DocPad really won me over. There is no database as everything is stored as files, documents can come in any format or shape and the community is very vibrant (as any Node community these days) just to name a few of the selling points. It is the perfect platform for blogs so the decision was not very hard.</p>

<p>The switch also gave me a chance of redesigning the blog entirely. I was previously using a very old version of Blogger since I did not want to convert the templates to the new Blogger format as I would much rather use a new platform and spend my time on migrating to that. I used <a href="http://getbootstrap.com">Bootstrap</a> for the styling but strongly considered trying something more <a href="http://coding.smashingmagazine.com/2011/12/12/an-introduction-to-object-oriented-css-oocss/">OOCSS-like</a> just for the experience. The blog is now responsive and looks great on all the devices I tested so far.</p>

<p>I also introduced <a href="http://disqus.com">Disqus</a> as my comments framework. Previously I just used the built-in Blogger comments framework which worked but was not that exciting. So far I like Disqus although it has some quirks and some limits in regard to formatting.</p>

<p>The conclusion? I am pretty happy about the end result from more than one perspective. I have more control with my blog and the frameworks it uses. It is definitely a huge plus that my posts are now written as markdown documents removing (most) formatting and styling from the document itself leaving all styling to my templates and stylesheets. Overall a huge win, I would definitely consider DocPad for my future projects.</p>]]></content><author><name>Peter Haldbæk</name></author><category term="docpad" /><category term="node" /><summary type="html"><![CDATA[Today I relaunched my blog. I wanted to skip the Blogger platform for something more flexible for a long time and now I have finally made the switch. I examined a lot of Node blogging platforms and decided to go with DocPad. I looked at other alternatives such as Wheat and Blacksmith but the simplicity of DocPad really won me over. There is no database as everything is stored as files, documents can come in any format or shape and the community is very vibrant (as any Node community these days) just to name a few of the selling points. It is the perfect platform for blogs so the decision was not very hard.]]></summary></entry><entry><title type="html">Find Antenna now available on iPhone</title><link href="http://www.briskbee.com/2013/12/find-antenna-now-available-on-iphone.html" rel="alternate" type="text/html" title="Find Antenna now available on iPhone" /><published>2013-12-11T00:00:00+00:00</published><updated>2013-12-11T00:00:00+00:00</updated><id>http://www.briskbee.com/2013/12/find-antenna-now-available-on-iphone</id><content type="html" xml:base="http://www.briskbee.com/2013/12/find-antenna-now-available-on-iphone.html"><![CDATA[<p>Finally my first iPhone app has hit the stores. The iPhone version of Find Antenna is available for download from the App Store. The functionality is the same as the Android version with some minor differences. I put a lot of work into the visual appearance of both app and icon to give a clearer visual identity. I also changed the layout of the app a little bit by using a scrollview instead of gesture recognizers to switch between transmitters. Representation of the signal strength was also changed to give a clearer indication on how to interpret the strength of the signal.</p>

<p><img src="/assets/img/iphone-screenshot.png" alt="" /></p>

<p><a href="http://itunes.com/apps/findantenna"><img src="/assets/img/appstore.png" alt="" /></a></p>

<p>The first version of the app only contains Danish transmitters but more countries are expected to be supported shortly. I will add Sweden, Germany, United Kingdom and Australia in the near future. If you want your country added or have information on transmitters not on the list do not hesitate to contact me.</p>

<p>The app is available at the App Store so go get it while it’s hot!</p>]]></content><author><name>Peter Haldbæk</name></author><category term="findantenna" /><category term="iphone" /><category term="ios" /><summary type="html"><![CDATA[Finally my first iPhone app has hit the stores. The iPhone version of Find Antenna is available for download from the App Store. The functionality is the same as the Android version with some minor differences. I put a lot of work into the visual appearance of both app and icon to give a clearer visual identity. I also changed the layout of the app a little bit by using a scrollview instead of gesture recognizers to switch between transmitters. Representation of the signal strength was also changed to give a clearer indication on how to interpret the strength of the signal.]]></summary></entry><entry><title type="html">Find Antenna officially released</title><link href="http://www.briskbee.com/2013/02/find-antenna-officially-released.html" rel="alternate" type="text/html" title="Find Antenna officially released" /><published>2013-02-28T00:00:00+00:00</published><updated>2013-02-28T00:00:00+00:00</updated><id>http://www.briskbee.com/2013/02/find-antenna-officially-released</id><content type="html" xml:base="http://www.briskbee.com/2013/02/find-antenna-officially-released.html"><![CDATA[<p>My very first mobile app was officially released yesterday. I have spent many evenings on ridiculous details and I am happy to say that the app is ready at last. The app can be used to determine the 5 closest DVB-T transmitters and in which direction they can be found. It is very useful if you want to figure out which way your TV antenna should point. It uses the location of the mobile phone to determine which transmitters are closest and the compass to determine the direction.</p>

<p><img src="/assets/img/android-screenshot.png" alt="" />
<a href="https://play.google.com/store/apps/details?id=com.briskbee.master"><img src="https://developer.android.com/images/brand/en_generic_rgb_wo_60.png" alt="" /></a></p>

<p>The app is currently only available for Android. You can find at Google Play by clicking the icon above.</p>]]></content><author><name>Peter Haldbæk</name></author><category term="findantenna" /><category term="android" /><summary type="html"><![CDATA[My very first mobile app was officially released yesterday. I have spent many evenings on ridiculous details and I am happy to say that the app is ready at last. The app can be used to determine the 5 closest DVB-T transmitters and in which direction they can be found. It is very useful if you want to figure out which way your TV antenna should point. It uses the location of the mobile phone to determine which transmitters are closest and the compass to determine the direction.]]></summary></entry><entry><title type="html">Setting character set to UTF-8 in MySQL</title><link href="http://www.briskbee.com/2012/10/setting-character-set-to-utf-8-in-mysql.html" rel="alternate" type="text/html" title="Setting character set to UTF-8 in MySQL" /><published>2012-10-25T00:00:00+00:00</published><updated>2012-10-25T00:00:00+00:00</updated><id>http://www.briskbee.com/2012/10/setting-character-set-to-utf-8-in-mysql</id><content type="html" xml:base="http://www.briskbee.com/2012/10/setting-character-set-to-utf-8-in-mysql.html"><![CDATA[<p>The default installation of MySQL (5.5) does not use UTF-8 so every time I install an instance of MySQL I spend some time tinkering with the character setup so I decided to write a quick summary of the steps needed in order for MySQL to support UTF-8.  When you have a vanilla installation of MySQL you can check your character settings by executing these commands.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql&gt; show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

mysql&gt; show variables like 'collation%';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | utf8_general_ci   |
| collation_database   | latin1_swedish_ci |
| collation_server     | latin1_swedish_ci |
+----------------------+-------------------+
</code></pre></div></div>

<p>As we can see latin1 is used in several places. This should be changed to UTF-8. You can do this by either editing your existing my.cnf configuration file or creating a new configuration file in the conf.d folder of your MySQL configuration (recommended). I create a new configuration file at conf.d/utf8.cnf which looks like this.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[mysqld]
character-set-server = utf8
character-set-client = utf8
</code></pre></div></div>

<p>Restart the server.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; sudo service mysql restart
</code></pre></div></div>

<p>Log in to MySQL and check that everything is ok.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql&gt; show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

mysql&gt; show variables like 'collation%';
+----------------------+-----------------+
| Variable_name        | Value           |
+----------------------+-----------------+
| collation_connection | utf8_general_ci |
| collation_database   | utf8_general_ci |
| collation_server     | utf8_general_ci |
+----------------------+-----------------+
</code></pre></div></div>

<p>Everything is now using UTF-8.</p>

<h2 id="update-21112012">Update 21.11.2012</h2>

<p>If you want to use utf8_unicode_ci instead of utf8_general_ci (you probably
want that, see
<a href="http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci">this</a>)
then add the following to the my.cnf file in the mysqld section and restart the
server.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>collation-server = utf8_unicode_ci
</code></pre></div></div>

<h2 id="update-21112012-a-little-later">Update 21.11.2012 (a little later)</h2>

<p>The collation settings should be something like this.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mysql&gt; show variables like 'collation%';
+----------------------+-----------------+
| Variable_name        | Value           |
+----------------------+-----------------+
| collation_connection | utf8_general_ci |
| collation_database   | utf8_unicode_ci |
| collation_server     | utf8_unicode_ci |
+----------------------+-----------------+
</code></pre></div></div>
<p>I previously had the impression that it should be <code>utf8_unicode_ci</code>
for all collation variables (obtained by setting the
<code>skip-character-set-client-handshake</code> option in my.cnf) but this
generated weird behavior when comparing dates in the database.</p>]]></content><author><name>Peter Haldbæk</name></author><category term="mysql" /><category term="database" /><summary type="html"><![CDATA[The default installation of MySQL (5.5) does not use UTF-8 so every time I install an instance of MySQL I spend some time tinkering with the character setup so I decided to write a quick summary of the steps needed in order for MySQL to support UTF-8. When you have a vanilla installation of MySQL you can check your character settings by executing these commands.]]></summary></entry><entry><title type="html">Creating insert statements from table data</title><link href="http://www.briskbee.com/2012/10/creating-insert-statements-from-table-data.html" rel="alternate" type="text/html" title="Creating insert statements from table data" /><published>2012-10-23T00:00:00+00:00</published><updated>2012-10-23T00:00:00+00:00</updated><id>http://www.briskbee.com/2012/10/creating-insert-statements-from-table-data</id><content type="html" xml:base="http://www.briskbee.com/2012/10/creating-insert-statements-from-table-data.html"><![CDATA[<p>Creating a SQL script which inserts data from an already existing table is not as straightforward as one would think. I found a pretty clean and easy solution which I will explain in detail here. I use MySQL but I believe the principles can be applied to most other databases if needed.  Consider table A with the following content.</p>

<table>
  <thead>
    <tr>
      <th>Id</th>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Peter</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Jake</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Paul</td>
    </tr>
  </tbody>
</table>

<p>I want to create a script inserting all entries where name starts with P. The first step is to create a table containing these entries.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CREATE TABLE b (SELECT FROM a WHERE name LIKE 'P%');
</code></pre></div></div>
<p>Now the data can be exported using mysqldump.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; mysqldump -p -u &lt;username&gt; &lt;database&gt; b &gt; insert-script.sql
</code></pre></div></div>

<p>The created script contains statements which will drop and create the table (among other things). Normally I am only interested in the part where the insert statements are so open the created script file and remove everything except the <code>INSERT INTO</code> statement. Also remember to rename the name of the table where the data is to be inserted to the correct table name (table A in my case).  The final script file should look something like this.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>INSERT INTO a VALUES (1, 'Peter'),('3', 'Paul');
</code></pre></div></div>

<p>Finally you should drop the temporary table you created.</p>]]></content><author><name>Peter Haldbæk</name></author><category term="sql" /><category term="mysql" /><category term="database" /><summary type="html"><![CDATA[Creating a SQL script which inserts data from an already existing table is not as straightforward as one would think. I found a pretty clean and easy solution which I will explain in detail here. I use MySQL but I believe the principles can be applied to most other databases if needed. Consider table A with the following content.]]></summary></entry></feed>