Automated vCard writer (with photo)

Since I posted this yesterday (in the wrong thread naturally…), I’ve been obsessing a little over trying to cram as much data and as best a photo as possible on a limited size NTAG. Classic hacker pursuit: there isn’t nearly enough enough space on an NFC tag for a decent vCard photo, so I just had to do it :slight_smile:

I’ve been trying different JPEG encoders, different mugshot sizes, contrasts etc… I played the what-if game manually for a while - that is, change something, regenerate the vCard by hand, program it onto the tag by hand, try it on the cellphone, failure, rinse, repeat.

That got old pretty quickly, so I wrote a script to do everything in one go, test the size of the final NDEF and write it onto a tag. It makes trying to exploit as much of the tag’s memory space as possible quite easy and fast. So I figured I’d share it, if anybody’s interested.

The script is Linux-only. Sorry, if you have another OS, I can’t help you. But if you do run Linux, read on.

Before you use it, you need a few things installed:

  • Imagemagick: it should come installed by default in most Linux distros. If not, you can do something like apt-get install imagemagick as root (for Debian-based distros - your mileage may vary)

  • Guetzli: this is an advanced JPEG encoder released by Google in 2017 that really cuts down on size and preserve the quality a lot better than libjpeg. It’s not as good as JPEG2000, but JPEG2000 files aren’t supported natively in Android, so it wasn’t an option anyway. Guetzli really is quite good, and by far the best JPEG encoder I’ve found.

    There is a pre-built Guetzli package, but unfortunately, by default Guetzli refuses to lower the JPEG quality enough and tells you to get the source and hack it :frowning: So what you need to do is this (as an ordinary user):

    • Get the code from Github: git clone https://github.com/google/guetzli.git
    • Edit guetzli/guetzli/processor.cc, look for a line that says if (params.butteraugli_target > 2.0f) { and replace it with if (0) {
    • Compile it normally with make. No need to install it, the script below assumes it’s in the current directory
  • ndeflib: it’s a Python module. Install it by doing pip install ndeflib and pip3 install ndeflib as root (you need both the Python2 and Python3 versions).

  • ndeftool: another Python utility. Install it by doing pip3 install ndeftool as root.

  • nfcpy: do pip3 install nfcpy as root.

  • tagtool.py: normally it’s part of nfcpy, but it’s not packaged in the pip distribution for some reason. You need to get it from the Github repo. Simply do git clone https://github.com/nfcpy/nfcpy.git in the same directory you pulled Guetzli in as a normal user.

Finally, to create the vCard, encode it into an NDEF record and write it to a tag, copy / paste the following script in the directory where Guetzli and nfcpy reside:

#!/bin/bash

### vCard information
FIRSTNAME=Joe
LASTNAME=Blow
TELEPHONE="(311) 555-1212"
EMAIL=joeblow@aol.com
PHOTO=./photo.jpg
NOTE=

### Final size of the photo on the tag
TAG_PHOTO_SIZE=50x50

### Quality of the compressed photo on the tag
JPEG_QUALITY=72

### Path to the various utilities we need
GUETZLI=./guetzli/bin/Release/guetzli
NDEFTOOL=ndeftool
TAGTOOL="python3 nfcpy/examples/tagtool.py"
 


# Resize / compress the original photo
echo "Resizing the photo"
convert $PHOTO -resize $TAG_PHOTO_SIZE ./photo_resized.png

echo "Compressing the photo"
$GUETZLI --quality $JPEG_QUALITY ./photo_resized.png ./photo_compressed.jpg

# Create the .vcf file
echo "Creating the vCard file"

echo "BEGIN:VCARD" > vcard.vcf
echo "VERSION:2.1" >> vcard.vcf
echo "N:$LASTNAME;$FIRSTNAME" >> vcard.vcf
echo "TEL:$TELEPHONE" >> vcard.vcf
echo "EMAIL:$EMAIL" >> vcard.vcf

B64ENCPHOTO=$(base64 -w0 ./photo_compressed.jpg)
B64ENCPHOTO="PHOTO;ENCODING=BASE64;TYPE=JPEG:$B64ENCPHOTO"
echo $B64ENCPHOTO | head -c 73 >> vcard.vcf
for L in $(echo $B64ENCPHOTO | tail -c +74 | sed -e 's/.\{72\}/&\n/g'); do
  echo -en "\n $L" >> vcard.vcf
done
echo >> vcard.vcf

if [ "$NOTE" ];then
  echo "NOTE:$NOTE" >> vcard.vcf
fi

echo "END:VCARD" >> vcard.vcf

# Dump the size of the .vcf file, for information
VCARDSIZE=$(wc -c < vcard.vcf)
echo "vCard size: $VCARDSIZE bytes"

# Create the NDEF record
echo "Encapsulating the vCard into an NDEF record"
$NDEFTOOL tn text/vcard pl "$(cat vcard.vcf)" > vcard.ndef

# Dump the size of the NDEF file, for information
NDEFSIZE=$(wc -c < vcard.ndef)
echo "NDEF size: $NDEFSIZE bytes"

# Read the tag
echo "Reading the tag"
CARD_INFO=$($TAGTOOL)

# Make sure the tag is writable
if [ ! "$(echo $CARD_INFO | grep -i 'NDEF Capabilities.*writeable *= *yes')" ]; then
  echo "Tag is not writeable!"
  exit -1
fi

# Make sure there's enough room on the tag for the NDEF record
TCAPA=$(echo $CARD_INFO | sed -r 's/^.*NDEF Capabilities.*capacity *= *([0-9]+) byte.*$/\1/')
if [ $TCAPA -lt $NDEFSIZE ]; then
  echo "NDEF size exceeds the tag's capacity ($TCAPA bytes)!"
  exit -1
fi

# Ask the user if they really want to write the vCard on the tag
read -p "Write NDEF onto the tag [Y/N]? " YN
if [ "$YN" != "y" ] && [ "$YN" != "Y" ]; then
  echo "Aborting"
  exit 0
fi

# Write the NDEF file onto the tag
echo "Writing NDEF onto the tag"
$TAGTOOL load vcard.ndef

echo "All done!"

Name it, say, write_vcard_to_tag.sh, then make it executable by doing chmod +x write_vcard_to_tag.sh.

To use it, supply a photo - preferably with a square aspect ratio - called photo.jpg. Then edit your vCard information at the beginning of the script, and run it. It’ll generate the vCard, then the NDEF record, then try to write it onto the tag.

If the NDEF record ends up being too large, it’ll tell you and stop. If that happens, go back to the script and tweak the size of the photo and the JPEG quality until it fits and you’re satisfied with the resulting image.

The aforementioned utilities use libnfc to write to the tag. I’ve only tried it with an ACR122U, but I suppose it’ll work just as well with other readers supported by libnfc. In any case, if you don’t have that going yet, you’ll have to set it up first.

After playing with it for a while, I managed to come up with a vCard that contains my personal information, a 50x50 photograph in which I’m reasonably recognizable even after blowing it up, and my blood type in the vCard’s note field, with only 2 bytes to spare on an NTAG216 :slight_smile:

That’ll be the vCard I’ll be putting on my doNExT: it’ll serve for snazzy contact exchange of course, but also for first-aid professionals if I’m found unconscious somewhere and they happen to carry a cellphone (and can I convince myself to have an NFC logo tattoo done over the implant… Not too sure about that one yet).

I hope the above will be of interest.

3 Likes

I’m sure it will, Great share,
Thanks very much.

50x50=

or

image

1 Like

Yeah but the quality is a lot more shite than those clean icons. Remember, it’s an overly compressed JPEG: it’s blurry and full of compression artifacts. Not much you can do in 525 bytes. Still, it looks surprisingly good as a small image on a cellphone.

This is the photo I have on my vCard (blurry enough that I don’t mind posting it too much, for a change :slight_smile:):

photo_compressed

Awesome, I was going to ask, but didn’t think you would.

I hope you don’t mind, I blew it up,
and it made you look like a character from Guess Who

image image

1 Like

Looks like I shouldn’t’ve :slight_smile:

Never gets old. :ok_hand:

1 Like

I’ve done some more research and testing, and I found a two ways to increase photo quality even more:

1/ Decrease the image saturation: the end result looks less colorful, but the size of the compressed image decreases significantly

2/ Change file format from JPEG to WEBP. I’ve done tests on a few Android cellphone, and it turns out they all support WEBP natively down to at least Android v5. And boy! is WEBP better than JPEG or what! For the same final size, a WEBP image has a higher resolution, and much fewer compression artifacts than a JPEG.

Here for example, a photo of our Glorious Leader, before (JPEG, 686 x 686, 315 KB) and after (WEBP, 60 x 60, 555 bytes - fits on an NTAG 216):

It looks fantastic on a cellphone. Fucking brilliant image format: it really blows JPEG out of the water.

So, here’s an updated version of my script, that does both JPEG and WEBP - but does WEBP by default. All you gotta do is stick your photo as a photo.jpg file, edit your information, and play around with the compression parameters until the NDEF fits on your tag. In addition to the packages listed in the first post of this thread, you’ll need the webp package (apt-get install webp should do the trick):

#!/bin/bash

### vCard information
FIRSTNAME=Joe
LASTNAME=Blow
TELEPHONE=(311)555-1212
EMAIL=joeblow@aol.com
PHOTO=./photo.jpg
NOTE=

### What type of photo encoding/compression to use (JPEG or WEBP)
PHOTO_ENCODING=WEBP

### Parameters for the final JPEG image to put in the vCard, if we do JPEG
JPEG_SIZE=52x52
JPEG_SATURATION=43
JPEG_QUALITY=73

### Parameters for the final WEBP image to put in the vCard, if we do WEBP
WEBP_SIZE=60x60
WEBP_SATURATION=56
WEBP_QUALITY=69

### Path to the various utilities we need
GUETZLI=./guetzli/bin/Release/guetzli
CWEBP=cwebp
NDEFTOOL=ndeftool
TAGTOOL="python3 nfcpy/examples/tagtool.py"
 


# Resize / desaturate the original photo
echo "Resizing the photo for JPEG"
convert $PHOTO -resize $JPEG_SIZE -modulate 100,$JPEG_SATURATION,100 \
	./photo_resized_for_jpeg.png

echo "Resizing the photo for WEBP"
convert $PHOTO -resize $WEBP_SIZE -modulate 100,$WEBP_SATURATION,100 \
	./photo_resized_for_webp.png

echo "Compressing the photo for JPEG"
$GUETZLI --quality $JPEG_QUALITY ./photo_resized_for_jpeg.png \
	./photo_compressed.jpg

echo "Compressing the photo for WEBP"
$CWEBP -q $WEBP_QUALITY ./photo_resized_for_webp.png -o \
	./photo_compressed.webp

# Create the .vcf file
echo "Creating the vCard file"

echo "BEGIN:VCARD" > vcard.vcf
echo "VERSION:2.1" >> vcard.vcf
echo "N:$LASTNAME;$FIRSTNAME" >> vcard.vcf
echo "TEL:$TELEPHONE" >> vcard.vcf
echo "EMAIL:$EMAIL" >> vcard.vcf

if [ $PHOTO_ENCODING = JPEG ];then
  B64ENCPHOTO=$(base64 -w0 ./photo_compressed.jpg)
else
  B64ENCPHOTO=$(base64 -w0 ./photo_compressed.webp)
fi
B64ENCPHOTO="PHOTO;ENCODING=BASE64;TYPE=$PHOTO_ENCODING:$B64ENCPHOTO"
echo $B64ENCPHOTO | head -c 73 >> vcard.vcf
for L in $(echo $B64ENCPHOTO | tail -c +74 | sed -e 's/.\{72\}/&\n/g'); do
  echo -en "\n $L" >> vcard.vcf
done
echo >> vcard.vcf

if [ "$NOTE" ];then
  echo "NOTE:$NOTE" >> vcard.vcf
fi

echo "END:VCARD" >> vcard.vcf

# Dump the size of the .vcf file, for information
VCARDSIZE=$(wc -c < vcard.vcf)
echo "vCard size: $VCARDSIZE bytes"

# Create the NDEF record
echo "Encapsulating the vCard into an NDEF record"
$NDEFTOOL load --pack vcard.vcf tn text/vcard id '' save --force vcard.ndef

# Dump the size of the NDEF file, for information
NDEFSIZE=$(wc -c < vcard.ndef)
echo "NDEF size: $NDEFSIZE bytes"

# Read the tag
echo "Reading the tag"
CARD_INFO=$($TAGTOOL)

# Make sure the tag is writable
if [ ! "$(echo $CARD_INFO | grep -i 'NDEF Capabilities.*writeable *= *yes')" ]; then
  echo "Tag is not writeable!"
  exit -1
fi

# Make sure there's enough room on the tag for the NDEF record
TCAPA=$(echo $CARD_INFO | sed -r 's/^.*NDEF Capabilities.*capacity *= *([0-9]+) byte.*$/\1/')
if [ $TCAPA -lt $NDEFSIZE ]; then
  echo "NDEF size exceeds the tag's capacity ($TCAPA bytes)!"
  exit -1
fi

# Ask the user if they really want to write the vCard on the tag
read -p "Write NDEF onto the tag [Y/N]? " YN
if [ "$YN" != "y" ] && [ "$YN" != "Y" ]; then
  echo "Aborting"
  exit 0
fi

# Write the NDEF file onto the tag
echo "Writing NDEF onto the tag"
$TAGTOOL load vcard.ndef

echo "All done!"

Sorry, Linux-only. I don’t have Windows.

5 Likes

That IS impressive, Amal doesn’t even look like a Guess Who™ character!

Nice work @anon3825968

Yeah I’m properly impressed by the quality for a half kilobyte image.

I didn’t think cellphones read anything other than JPEG and GIF for contact photos. But then I remembered Android is Google, and WEBP is also Google. I figured it would make sense for them to have Android support it. So I tried, and sure enough… tada!

1 Like

This is super cool, I plan to create and upload one tonight :slight_smile:

I wish I was smart enough to turn this into an automated webpage app thing…

Thanks again!!!

1 Like

Just a couple notes;

From a fresh Kali Live, to compile, you also need:
apt-get install libpng-dev
apt-get install pkg-config

In your updated script, you forgot the " " around the phone number and the script spits out an error because of the ( ) around the area code.

Lastly, because I’m a noob, the max size of the output file can be 888 bytes. Make sure you adjust the WEBP section of the quality settings, not just the top one. That threw me off for a few mins :slight_smile:

Sorry, I’m getting an error when trying to open the vCard on my Pixel 2

Ooh, well spotted. That one’s on me: I replaced my personal phone number with a generic one in the script before pasting it here and I didn’t check that it would work. I used the first fake number that came to my mind, but I currently live in Finland and there are no parens in phone numbers here :slight_smile: Sorry about that.

That’s the maximum size of the NDEF (more like 868 bytes for a NTAG216 in fact, because there are bytes used by the NDEF encapsulation and other reserved fields in the tag). But the actual image in binary format is much smaller, because it needs to be base64-encoded. Typically in the order of 5.5 kB before encoding.

Yes sorry again, I guess that wasn’t too clear. There’s a top PHOTO_ENCODING variable that selects whether you want JPEG or WEBP, and then two distinct quality settings. If PHOTO_ENCODING is set to WEBP, only the WEBP_* variables do anything.

Hmm now that’s strange.

There are two things your phone could choke on: either the version of the vCard format - which I set at 2.1 - or the WEBP encoding of the image.

vCard v2.1 dates back to 1996, so it should be supported. I chose it because that’s the format that has the fewer mandatory fields, to save as much space as possible. Also, it’s a sort of lowest common denominator, to ensure it’s compatible with just about anything. You could try vCard v3.0, but then you need to add other fields like FN and ADR for full compatibility. See here: https://www.evenx.com/vcard-3-0-format-specification

WEBP is a Google format that was created in 2010. Being a Google format, it’s highly likely to be understood natively by all Android phones since 2010. Yours was made in 2017: it most definitely should understand it. I myself tried it on Android versions from 5 to 10 without any issues, and I shared my own vCard with many people without any issues either. Maybe try to transfer the .webp image as-is onto the phone’s filesystem and open it directly with the photo viewer: if it displays it, there should be no issue with the image in the vCard, since it’s the same code that handlies both.

I can send you a ready-made .ndef file that I know works for sure, that you could try to write into a tag and try on your phone if you want, just to make sure there isn’t something odd with the Pixel 2. I wouldn’t think there is - it is a Google phone after all - but who knows.

Also, are you using the stock Android contact app?

I wish all noobs were as competent as you :slight_smile:

And also because of the capability container nonsense not making full use of the memory for NFC… it’s honestly pretty fucked up.

The nonsense only applies to chips with more than 1k though doesn’t it? The NTAG216 isn’t really concerned by this.

Also, aren’t most DT implants magic? If it’s magic, it should work everywhere with the power of magic :slight_smile:

magic-confetti

Once again, noob here, but I don’t think many are ‘magic’ (sorry if that was sarcasm), especially the NeXT (which I assume is a top seller?). Something about the magic ones being physically larger.

Something I wish I would have known more about when I got the NeXT, but no biggie

Yes, I can view it just fine, and if it’s OVER 50x50px I can add it to a vCard (it wants to give you the option to crop it before adding it, but can’t edit under 50x50px), but if you try to export it from there, it shows the vCard as being like 2k or something (probably the vCard v3 or whatever?).

Yes, all stock Google Pixel 2, no branding

Well shucks :stuck_out_tongue:
Just trying to help document as much as possible for other future noobs.

Okay here’s a corrected version. I removed the confusing JPEG bit (it just does WEBP compression) and I made sure it works this time :slight_smile: I also left the intermediate files it produces and the final NDEF file, in case you want to compare what it does on your machine.

Naturally, I’ve tested it and it works: the tag is an NTAG216, and it works fine on my Android 5, 7, 9 and 10 test phones.

vcard_writer_with_sample_information.zip (333.7 KB)

Let me know how this works for you.

No… read the section called Memory problems

Not sarcasm. There are some DT implants with magic chips in the sense that they can be configured to change personality or they have backdoor commands. But all DT implants are magic because they were lovingly crafted by Amal and they bring you joy. Also, they automatically expose you to the DT worldwide mind control field and it’s great, and there are no…no…no…no…no…no…no…side…side…side…side…effecttttttttsttststtsttstt&"#¤&%/