logo
Antony Jepson
🤔 About
✍️ Contact
21 Sep 2020
 

Rotating expired Nitrokey subkeys used for password store
5 July 2020

I’m currently managing my saved passwords with a mixture of pass and Nitrokey. One of my sub-keys expired so I couldn’t update my passwords. Here’s how I generated new keys (rotated them) with a new expiration date.

You’ll need access to your master key. Most tutorials online will make you generate the key locally, nerf it, and upload it into the Nitrokey. In this case, you’ll need find the original primary signing key before moving forward.

Once you have it in hand, extract the contents to a temporary directory and let’s begin. Don’t forget to set the directory permissions appropriately (chmod 700 ./). We’ll be using gpgh to refer to this new directory we’re using for gpg.

$ alias gpgh="gpg --homedir $(pwd)"

$ gpgh --import user@domain.tld.gpg-private-keys
< enter password >

Trust the keys.
$ gpgh --edit-key user@domain.tld
gpg> trust

< select 5 for maximum trust >
< select y to confim >
< exit to confirm >

Modify expiry date of primary key.
$ gpgh --expert --edit-key user@domain.tld
gpg> expire

< select and confirm a new timeframe >

Generate new subkeys.
gpg> list
sec brainpoolP384r1/DEADBEEFDEADBEE1
created: 2019-XX-XX expires: 2021-XX-XX usage: SC
trust: ultimate validity: ultimate
ssb brainpoolP384r1/DEADBEEFDEADBEEA
created: 2019-XX-XX expired: 2020-XX-XX usage: E
ssb brainpoolP384r1/DEADBEEFDEADBEEB
created: 2019-XX-XX expired: 2020-XX-XX usage: S
ssb brainpoolP384r1/DEADBEEFDEADBEEC
created: 2019-XX-XX expired: 2020-XX-XX usage: A
[ultimate] (1). User Name

Generate new subkeys
gpg> addkey
< select 12 to replace subkey BEEA >
< select 7 for brainpool p-384 >
< select 6m for six months >
< select y then y to confirm >
gpg> addkey
< select 10 to replace subkey BEEB >
< select 7 for brainpool p-384 >
< select 6m for six months >
< select y then y to confirm >
gpg> addkey
< select 11 to replace subkey BEEC >
< select A to toggle authenticate capability >
< select S to toggle authenticate capability >
< select Q to finish >
< select 7 for brainpool p-384 >
< select 6m for six months >
< select y then y to confirm >

Remove the expired subkeys
gpg> key 1
gpg> key 2
gpg> key 3
gpg> delkey

Export private and public keys to prepare for backup.
$ gpgh --armor --export-secret-keys user@domain.tld > user@domain.tld-private-keys-2020-07-05
$ gpgh --armor --export user@domain.tld > user@domain.tld-public-keys-2020-07-05

Generate a new revocation certificate
$ gpgh --gen-revoke user@domain.tld > user@domain.tld.gpg-revocation-certificate-2020-07-05

Encrypt the private keys, public keys, and revocation certificate in a symmetrically encrypted tarball and send to offsite.
$ tar cf ./user@domain.tld-keys-2020-07-05.tar user@domain.tld-*-2020-07-05
$ gpgh --symmetric --cipher-algo aes256 user@domain.tld-keys-2020-07-05.tar
$ rm user@domain.tld-keys-2020-07-05.tar
$ sendoffsite user@domain.tld-keys-2020-07-05.tar.gpg
$ sendoffsite user@domain.tld-public-keys-2020-07-05

Import new subkeys into Nitrokey, replacing existing subkeys
< plug in Nitrokey>

$ gpgh --expert --edit-key user@domain.tld
gpg> key 1
gpg> keytocard

< select 2 for encryption key >
< enter master key password >
< enter admin pin for nitrokey >
gpg> key 1
gpg> key 2
gpg> keytocard

< select 1 for signature key >
< enter master key password >
gpg> key 2
gpg> key 3
gpg> keytocard

< select 3 for authentication key >
< enter master key password >
gpg> save

(Note down the encryption key from the “list” output so you can re-initialise pass later.)

Kill the running GPG agents that might interfere with password caching.
$ gpgconf --kill gpg-agent
$ GNUPGHOME=$(pwd) gpgconf --kill gpg-agent

Confirm those sneaky buggers are gone.
$ ps aux | grep gpg

Migrate your pass store to the new set of keys. You’ll need to do this with both the old and new set of keys accessible so we’ll run this from our temporary directory with the expired sub-keys.

First cache the password of the private key.
$ echo "test message string" | gpgh --encrypt --armor --recipient user@domaind.tld -o encrypted.txt
$ gpgh --decrypt --armor encrypted.txt

Confirm you can decrypt an existing pass key.
$ gpgh --decrypt ~/.password-store/some/key/user@domain.tld.gpg

Backup pass directory
$ cp -R ~/.password-store ~/.password-store_bak

Next migrate the passwords, using the encrypted subkey we listed above.
$ PASSWORD_STORE_GPG_OPTS="--homedir $(pwd)" pass init DEADBEEFDEADBEEFD

Create and delete a fake password to confirm it’s working.
$ PASSWORD_STORE_GPG_OPTS="--homedir $(pwd)" pass generate fake/password
$ PASSWORD_STORE_GPG_OPTS="--homedir $(pwd)" pass edit fake/password
$ PASSWORD_STORE_GPG_OPTS="--homedir $(pwd)" pass rm fake/password

Finally, update your local GPG configuration by importing the new public keys. Notice we’re using the normal gpg. You should see 3 new subkeys imported.
$ gpg --import user@domain.tld-public-keys-2020-07-05

Now you can remove the temporary directory you made after confirming you’ve backed up the encrypted backup and also published the public keys somewhere accessible.
$ rm -r $(pwd)
$ cd

More articles I’ve written on this topic:
* Using GPG to master your identity (Part 1)
* Configuring and integrating Nitrokey into your workflow

Generating smooth sine tone output on iOS
20 December 2018

It has been several years since I studied signal processing in depth. I’ve been working on a pet project recently and needed to generate a sine tone on iOS. I almost gave up and just dragged in the AudioKit framework but decided it would be more rewarding to try and create it myself.

So — as a net positive, I’m going to describe how I built the sine tone generator. There’s probably some mistakes or inaccuracies in the mathematical notation or use of code even though it works to my satisfaction.

For the problem statement: generate a 200Hz sinusoidal PCM tone.

Theory

Let’s start with a simple sine wave, defined by y = sin(x).

We can see this wave has amplitude 1, a frequency of 1/(2 * pi) Hz, and a phase of 0, defined by the classic equation.

Now, when considering a 200Hz tone, the frequency or oscillations per second becomes 200. Note that we need to increase the number of points in the linear vector to account for the new frequency.

Great. In this figure, we show a one second representation of the sine waves output. Typically, for a CD quality PCM tone, we need to sample this one second representation 44100 times a second. As the frequency of this wave is only 200 Hz, we satisfy the sampling theorem.

Let’s zoom into the graph and see the sampling, also known as quantisation, of the wave.

Usually, audio is placed into buffers and then queued for playback. In this example, one buffer will be enqueued while the other provide smooth playback. The buffer is small to reduce memory impact.

Assuming each buffer is 512 samples, let’s see how the wave is partitioned after 0.05 seconds.

Here are the 5 buffers, represented by different coloured lines, that represent the segmentation of 0.05 seconds of the sinusoidal tone.

Code

Let’s try and build up to the above in a real code example. We’ll first build the skeleton in Swift Playgrounds to generate the tone and then we’ll optimise the execution.

We’ll use the AVAudio parts of the AVFoundation for the initial concept. We’ll be looking into the classes and subclasses of AVAudio.

Let’s initialise an engine that we’ll use for playback.

Next, we’ll create the audio buffer that we’ll use for the sine tone. We’ll also create a player node that we’ll use to pipe the audio to the engine and declare the format of the PCM buffer.

Sorted! Now, let’s generate the 200Hz tone, taking into account the theory above. Initially we’ll use a really large buffer and scale it back later. Note, we’re being a bit naughty here and assuming all the allocation succeeds.

Awesome. It sounds good albeit runs a bit slow in Playgrounds. We can vectorise the equation later for better performance. Before we abstract the implementation into something reusable, let’s try and use multiple buffers.

This was pretty simple. We just added another for loop to encapsulate the buffer generation.

Now, that we have the serial flow understood. We can now start building a class to simplify things.

This following code will play a tone given a certain frequency, phase, and amplitude. Here is the final code that generates a 200Hz tone for five seconds.

There’s more opportunity here to handle scheduling of multiple buffers, overlapping with system sounds, ramping of frequencies, etc but this works as a good first pass.


// Antony Jepson
// A simple sine tone generator
// Developed in Xcode 10.1 for iOS 12

import Foundation
import AVFoundation

public struct Constants {
static let pcmSampleRateFloat: Float = 44100.0
static let pcmSampleRateFloat32: Float32 = 44100.0
static let pcmSampleRateDouble: Double = 44100.0
static let defaultBufferLengthInSeconds: Double = 0.2
}

public enum ToneTypes {
case sine
case cosine
}

// Tone: representation of a sinusoidal wave
public struct Tone {
private let twoPi: Float32 = Float32.pi * 2
public var ToneType: ToneTypes = .sine
public var frequency: Float32 = 100.0
public var amplitude: Float32 = 0.2
public var phase: Float32 = 0.0

public func eval() -> Float32 {
switch ToneType {
case .sine:
return amplitude * sin(twoPi * frequency + phase)
case .cosine:
return amplitude * cos(twoPi * frequency + phase)
}
}
}

public class ToneGenerator {
private let audioBufferSize: AVAudioFrameCount = AVAudioFrameCount(Constants.pcmSampleRateDouble * Constants.defaultBufferLengthInSeconds)
private let audioFormat: AVAudioFormat = AVAudioFormat(standardFormatWithSampleRate: Constants.pcmSampleRateDouble, channels: 2)!
private var eng: AVAudioEngine = AVAudioEngine()
private var pn: AVAudioPlayerNode = AVAudioPlayerNode()
private var ab: AVAudioPCMBuffer
private var isPlaying: Bool = false
private var dq: DispatchQueue = DispatchQueue(label: “ToneGenerator”)
private var tone: Tone

init(tone: Tone) {
self.tone = tone
ab = AVAudioPCMBuffer(pcmFormat: audioFormat,
frameCapacity: audioBufferSize)!
eng.attach(pn)
eng.connect(pn, to:eng.mainMixerNode, format: audioFormat)

do {
try eng.start()
} catch {
print(“AVAudioEngine didn’t start.”)
}
}

private func fillBuffer(_ buffer: AVAudioPCMBuffer) -> Void {
var initialisedBuffer: [Float32] = Array(
stride(from: 0.0 as Float32,
through: Float32(self.audioBufferSize – 1),
by: 1.0 as Float32)
)

initialisedBuffer = initialisedBuffer.map {
(sampleSeekTime) -> Float32 in
return Tone(ToneType: self.tone.ToneType,
frequency: self.tone.frequency / Constants.pcmSampleRateFloat32 * sampleSeekTime,
amplitude: self.tone.amplitude,
phase: self.tone.phase).eval()
}

buffer.frameLength = self.audioBufferSize
buffer.floatChannelData![0].initialize(from: &initialisedBuffer,
count: Int(self.audioBufferSize))
buffer.floatChannelData![1].initialize(from: &initialisedBuffer,
count: Int(self.audioBufferSize))
}

private func scheduleLoopingBuffer(_ buffer: AVAudioPCMBuffer) -> Void {
pn.scheduleBuffer(buffer, at: nil, options: AVAudioPlayerNodeBufferOptions.loops) {
// code to execution upon completion
}
}

private func scheduleBuffer(_ buffer: AVAudioPCMBuffer) -> Void {
pn.scheduleBuffer(buffer) {
// code to execute upon completion
}
}

public func playLoop() {
dq.async {
self.fillBuffer(self.ab)
self.scheduleLoopingBuffer(self.ab)
self.pn.play()
}
}

public func playSingle() {
dq.async {
self.fillBuffer(self.ab)
self.scheduleLoopingBuffer(self.ab)
self.pn.play()
}
}

public func stop() {
pn.stop()
pn.reset()
}

deinit {
eng.stop()
}
}

let tone: Tone = Tone(ToneType: .sine,
frequency: 200,
amplitude: 0.1,
phase: 0.0)

let toneGenerator: ToneGenerator = ToneGenerator(tone: tone)

toneGenerator.playLoop()
sleep(5)
toneGenerator.stop()

Understanding SHA256 Part 3
13 May 2018

This is the last in the three part series where we break down the SHA-2 algorithm. In this part, we’ll improve the implementation shown in part 2 and compare it to some existing implementations.

Comparison

The main implementation I’d like to compare the C version I wrote is the sha256.c present in OpenSSL. Some of the main differences are highlighted below:

  • The square and cube fractional roots of the primes are pre-generated instead of being generated during runtime.
  • The [S|s]igma functions are defined instead of functions.
  • Typedef struct is used instead of referencing all structs by their long name.
  • They specify the rotate function in assembly instead of writing a C function.
  • They have a x86 assembly function that fetches the next block of bits.
  • They unroll the loop that fills the first 16 integers of the message schedule.

Most of the comments by these changes indicated they were for performance reasons.

Improvements to program

Based on the above I made the following changes to my program. This shaved off about a kilobyte from my program (before: 18656 bytes; now: 16840 bytes).

  • Removed all internal generation of prime fractional roots with constants.
  • Removed all internal debug printing messages.
  • Used defines instead of functions for the hash functions.
  • Moved all the overall SHA flow into its own function.
  • Made all references to the input string const.

The final program is below.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <inttypes.h>


static const unsigned long SQUARE_ROOT_256[8] = {
  0x6a09e667UL, 0xbb67ae85UL, 0x3c6ef372UL, 0xa54ff53aUL, 
  0x510e527fUL, 0x9b05688cUL, 0x1f83d9abUL, 0x5be0cd19UL
};

static const unsigned long CUBE_ROOT_256[64] = {
  0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
  0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
  0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
  0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
  0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
  0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
  0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
  0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
  0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
  0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
  0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
  0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
  0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
  0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
  0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
  0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
};

#define rotate_right(x, n)    (((x) >> (n)) | ((x) << (32 - (n))))
#define hash_sigma_0(x)       (rotate_right((x), 7) ^ rotate_right((x), 18) ^ ((x) >> 3))
#define hash_sigma_1(x)       (rotate_right((x), 17) ^ rotate_right((x), 19) ^ ((x) >> 10))
#define hash_sum_0(x)         (rotate_right((x), 2) ^ rotate_right((x), 13) ^ rotate_right((x), 22))
#define hash_sum_1(x)         (rotate_right((x), 6) ^ rotate_right((x), 11) ^ (rotate_right((x), 25)))
#define hash_maj(x, y, z)     (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define hash_ch(x, y, z)      (((x) & (y)) ^ ((~x) & (z)))

struct message_block {
  uint32_t block[16];
  struct message_block * next;
  uint8_t position;
};

struct message_schedule {
  uint32_t block[64];
  struct message_schedule * next;
};

struct message_block * new_message_block(void) {
  struct message_block * m = calloc(1, sizeof *m);
  m->next = NULL;
  return m;
}

void free_message_block(struct message_block *m) {
  if (m == NULL) return;
  free_message_block(m->next);
  free(m);
}

struct message_schedule * new_message_schedule(void) {
  struct message_schedule * m = calloc(1, sizeof *m);
  m->next = NULL;
  return m;
}

void free_message_schedule(struct message_schedule *m) {
  if (m == NULL) return;
  free_message_schedule(m->next);
  free(m);
}

struct hash {
  uint32_t value[8];
};

struct message_block * pad_message(const char *input) {
  size_t len = 0;
  uint32_t * blk;
  uint16_t remainder;
  char ch;
  struct message_block *head, *p;
  head = new_message_block();

  p = head;

  // Determine how many message blocks we need.
  // We need [data][1 bit][zero padding][length]
  // [data] maximum length 447 bits
  // [1 bit] length = 1 bit
  // [zero padding] maximum length 447 bits
  // [length] 64 bits

  if (input == NULL) {
    head->block[0] = 1u << 31;
    return head;
  }
  
  while (*(input + len) != '\0') {
    len++; // we have one character
    blk = &p->block[p->position];
    ch = *(input + len - 1);

    switch (len % 4) {
      case 1:  *blk |= ch << 24;
              break;
      case 2:  *blk |= ch << 16;
              break;
      case 3:  *blk |= ch << 8;
              break;
      default: *blk |= ch;
              break;
    }

    if (p->position == 15 && (len % 4) == 0) { // At the end of the block
      p->next = new_message_block();
      p = p->next;
    } else { 
      if (len % 4 == 0) p->position++;
    }
  }

  // Append the one after this block.
  p->block[p->position] += (1u << (32 - 8 * (len % 4) - 1));

  // Calculate how many zeroes is needed.
  remainder = (len * 8) % 512;
  if (remainder > 447) { // Get a new block
    p->next = new_message_block();
    p = p->next;
  }

  // Convert the length to count of binary.
  len *= 8;

  p->block[14] = (uint32_t) len >> 16;
  p->block[15] = (uint32_t) len;

  return head;
}


struct message_schedule * compute_message_schedule(struct message_block *mb) {
  uint8_t i;
  struct message_schedule *ms;

  if (mb == NULL)
    return NULL;

  ms = new_message_schedule();
  for (i = 0; i < 16; i++) {
    ms->block[i] = mb->block[i];
  }

  for (i = 16; i < 64; i++) {
    ms->block[i] = hash_sigma_1(ms->block[i - 2]) + ms->block[i - 7] + hash_sigma_0(ms->block[i - 15]) + ms->block[i - 16];
  }

  ms->next = compute_message_schedule(mb->next);

  return ms;
}

struct hash * perform_hash(struct hash *hash, struct message_schedule *m) {
  if (m == NULL) return hash;

  uint32_t a, b, c, d, e, f, g, h, tx, ty;
  uint8_t i;

  a = hash->value[0];
  b = hash->value[1];
  c = hash->value[2];
  d = hash->value[3];
  e = hash->value[4];
  f = hash->value[5];
  g = hash->value[6];
  h = hash->value[7];

  for (i = 0; i < 64; i++) {
    tx = h + hash_sum_1(e) + hash_ch(e, f, g) + CUBE_ROOT_256[i] + m->block[i];
    ty = hash_sum_0(a) + hash_maj(a, b, c);
    h = g;
    g = f;
    f = e;
    e = d + tx;
    d = c;
    c = b;
    b = a;
    a = tx + ty;
  }

  hash->value[0] += a;
  hash->value[1] += b;
  hash->value[2] += c;
  hash->value[3] += d;
  hash->value[4] += e;
  hash->value[5] += f;
  hash->value[6] += g;
  hash->value[7] += h;

  return perform_hash(hash, m->next);
} 

void sha256(const char *input) {
    struct message_block *h;
    struct message_schedule *s;
    struct hash * hash = calloc(1, sizeof *hash);
    unsigned int i;

    h = pad_message(input);

    for (i = 0; i < 8; i++)
      hash->value[i] = SQUARE_ROOT_256[i];

    s = compute_message_schedule(h);

    hash = perform_hash(hash, s);

    printf("Hash: ");
    for (i = 0; i < 8; i++)
      printf("%8x ", hash->value[i]);
    printf("\n");

    free_message_block(h);
}

int main(int argc, char *argv[]) {
    if (argc == 1) {
    sha256(NULL);
    } else {
    sha256(argv[1]);
    }
    
    return 0;
}
Getting Started with Symbian Development
1 May 2010

The world has gone app-crazy and I want to become a part of it. I’ve decided to see if I can horne my C++ skills by creating a Symbian application.

Getting prepared for Symbian development isn’t a straightforward process. Here’s how I set up my development environment:

Download and install ActivePerl (current version is 5.10.1).

I’m developing for the Symbian S60v3 FP1 platform so I needed to apply the context sensitive patch.

Download and install the JDK (current version 6u20).

Download and install the Carbine.c++ development environment (current version 2.3).

For Windows XP SP3 (and probably SP2) users, add the epoc.exe binary to the allowed DEP. You can do this by going to Start -> right click on My Computer -> click on Properties -> click on the Advanced tab -> click on Settings in the Performance category -> click on the Data Execution Prevention tab -> select the “Turn on DEP for all programs and services except those I select:” radio button -> click Add -> Navigate to C:\Symbian\9.2\S60_3rd_FP1\Epoc32\release\winscw\udeb and select epoc.exe (location is dependent on installation path).

Finally, install the S60 Platform SDK. I selected 3rd Edition, FP 1.

Further instructions are available at the Forum Nokia Library.

Managing gpg keys across multiple computers
14 June 2009

Manging a secret gpg key-pair across two computers is relatively simple.

Export the secret keys

gpg --export-secret-key -a your_email@address.com > myprivate.key

Export your public keys

gpg --export -a your_email@address.com > mypublic.key

Import the keys

gpg --import mypublic.key
gpg --import --allow-secret-key-import myprivate.key

That’s all there is to it.

Install a GNU/Linux distribution upon a USB Stick
24 May 2009

This is a quick reference-post that shows how to install GNU/Linux to a USB stick from within a GNU/Linux distribution.

Download the ISO or CD image.

[antony@ARCH arch-core]$ wget http://mirror.neotuli.net/arch/iso/2007.08/i686/Archlinux-i686-2007.08-2.core.iso -O ~/archlinux.iso

Plug in the USB stick.

[root@ARCH antony]# dmesg | tail

Oct 26 15:08:23 ARCH sd 2:0:0:0: [sdb] Mode Sense: 23 00 00 00
Oct 26 15:08:23 ARCH sd 2:0:0:0: [sdb] Assuming drive cache: write through
Oct 26 15:08:23 ARCH sd 2:0:0:0: [sdb] 3903488 512-byte hardware sectors (1999 MB)
Oct 26 15:08:23 ARCH sd 2:0:0:0: [sdb] Write Protect is off
Oct 26 15:08:23 ARCH sd 2:0:0:0: [sdb] Mode Sense: 23 00 00 00
Oct 26 15:08:23 ARCH sd 2:0:0:0: [sdb] Assuming drive cache: write through
Oct 26 15:08:23 ARCH sdb: sdb1
Oct 26 15:08:23 ARCH sd 2:0:0:0: [sdb] Attached SCSI removable disk
Oct 26 15:08:23 ARCH sd 2:0:0:0: Attached scsi generic sg1 type 0

Note the location of the drive (sdb).

Partition the USB stick.

[root@ARCH antony]# fdisk /dev/sdb

Then type the commands:

p – print partition table (count number of partitions, let’s call that n)

d – delete partition (type that n times)

n – add a new partition

p – create a primary partition

1 – make the primary partition the first partition

enter – default (first cylinder)

+512M – 512 megabytes from the first cylinder

n – new partition (create a second one for your files)

p – primary partition

2 – make this partition the second one

enter – default setting

enter – until the last cylinder on the USB stick

t – change partition type

1 – select partition 1

6 – partition code 6 (FAT16)

t – change partition type

2 – select partition 2

c – partition code c (FAT32 which is compatible with most file systems)

a – toggle bootable flag

1 – select partition 1

w – write table to disk and exit

Create the filesystem.

[root@ARCH antony]# mkfs.vfat -F 16 -n linux /dev/sdb1

[root@ARCH antony]# mkfs.vfat -F 16 -n vfat /dev/sdb2

Mount the ISO to a temporary directory.

[root@ARCH antony]# mkdir /mnt/iso

[root@ARCH antony]# mount -o loop /home/antony/archlinux.iso /mnt/iso

Copy the ISO’s contents to the USB stick.

[root@ARCH antony]# mkdir /mnt/usb

[root@ARCH antony]# mount /dev/sdb1 /mnt/usb

[root@ARCH antony]# cp -ra /mnt/iso/* /mnt/usb

Copy isolinux kernel to the root directory of the USB stick.

[root@ARCH antony]# cd /mnt/usb/isolinux

[root@ARCH antony]# cp vmlinuz ../

[root@ARCH antony]# cp initrd.img ../

[root@ARCH antony]# cp boot.* ../

[root@ARCH antony]# cp isolinux.cfg ../syslinux.cfg

Install the bootloader

[root@ARCH antony]# install-mbr /dev/sdb OR lilo -M /dev/sdb

[root@ARCH antony]# syslinux -s /dev/sdb1

[root@ARCH antony]# umount /mnt/usb

Built with Wordpress and Vim
© 2008 to 2020 Antony Jepson