Skip to content

A library to parse and generate Visible Digital Seals (VDS)

License

Notifications You must be signed in to change notification settings

tsenger/vdstools

Repository files navigation

VdsTools - Kotlin multiplatform library to work with Visible Digital Seals

License Maven Central Version GitHub Actions Workflow Status

This a Kotlin multiplatform (JVM and iOS) library to decode/verify and encode/sign Visible Digital Seals (VDS) as specified in

It also supports encoding and decoding Seals defined in the new draft of ICAO Datastructure for Barcode. Since release 0.9.0 ICAO IDB barcode encoder/decoders are fully functional. VDS and ICD barcodes can be parsed by a generic interface. An example is given in the following chapter

VDS can be created with the help of this library or, if you want to try it out quickly, via the web Sealgen tool. There is also the Sealva Android app which scans, verifies and displays all VDS profiles defined in the above specifications.

Get it on Google Play

Parse and verify a VDS / IDB

Here is a quick overview how to use the generic parser and verifier. In release 0.9.0 a generic interface was introduced to handle VDS and IDB barcode via common function calls. When you have the decoded raw string from your favorite datamatrix decoder, just put used the VDS Tools like this:

import de.tsenger.vdstools.Verifier
import de.tsenger.vdstools.vds.DigitalSeal
import de.tsenger.vdstools.vds.Feature

//Example for VDS / IDB barcode type
val seal: Seal = Seal.fromString(rawString)
val mrz: String? = seal.getMessage("MRZ")?.valueStr


// get all available Messages / Features in a List
val messageList: List<Message> = seal.messageList

for (message in messageList) {
    println("${message.name}, ${message.coding},  ${message.valueStr}")
}

// SignatureInfo contains all signature relevant data
val signatureInfo: SignatureInfo = seal.signatureInfo

// Get the VDS signer certificate reference
val signerCertRef: String = signatureInfo.signerCertificateReference

// Since X509 certificate handling is strongly platform-dependent, 
// the Verfifier is given the plain publicKey (r|s) and the curve name.
val publicKeyBytes: ByteArray = byteArrayOf()
val verifier: Verifier =
    Verifier(seal.signedBytes, signatureInfo.plainSignatureBytes, publicKeyBytes, "brainpoolP224r1")
val result: Verifier.Result = verifier.verify()

Build a barcode

Here is an example on how to use the DateEncoder and Signer classes to build a VDS barcode:

val keystore: KeyStore = ...

// In this JVM example we use a BouncyCastle keystore to get the certificate (for the header information)
// and the private key for signing the seals data
val cert: X509Certificate = keystore.getCertificate(keyAlias)
val ecKey: ECPrivateKey = keystore.getKey(certAlias, keyStorePassword.toCharArray())

// initialize the Signer
val signer: Signer = Signer(ecKey.encoded, curveName)

// 1. Build a VdsHeader
val header = VdsHeader.Builder("ARRIVAL_ATTESTATION")
    .setIssuingCountry("D<<")
    .setSignerIdentifier("DETS")
    .setCertificateReference("32")
    .setIssuingDate(LocalDate.parse("2024-09-27"))
    .setSigDate(LocalDate.parse("2024-09-27"))
    .build()

// 2. Build a VdsMessage
val mrz = "MED<<MANNSENS<<MANNY<<<<<<<<<<<<<<<<6525845096USA7008038M2201018<<<<<<06"
val azr = "ABC123456DEF"
val vdsMessage = VdsMessage.Builder(header.vdsType)
    .addDocumentFeature("MRZ", mrz)
    .addDocumentFeature("AZR", azr)
    .build()

// 3. Build a signed DigitalSeal
val digitalSeal = DigitalSeal(header, vdsMessage, signer)

// The encoded bytes can now be used to build a datamatrix (or other) code - which is not part of this library
val encodedSealBytes = digitalSeal.encoded

Here is an example on how to use the DateEncoder and Signer classes to build a IDB barcode:

val keystore: KeyStore = ...

// In this JVM example we use a BouncyCastle keystore to get the certificate (for the header information)
// and the private key for signing the seals data
val cert: X509Certificate = keystore.getCertificate(keyAlias)
val ecKey: ECPrivateKey = keystore.getKey(certAlias, keyStorePassword.toCharArray())

// initialize the Signer
val signer: Signer = Signer(ecKey.encoded, curveName)

// 1. Build a IdbHeader
val header = IdbHeader(
    "D<<",
    IdbSignatureAlgorithm.SHA256_WITH_ECDSA,
    DataEncoder.buildCertificateReference(cert.encoded),
    "2025-02-11"
)

// 2. Build a MessageGroup
val messageGroup = IdbMessageGroup.Builder()
    .addMessage(0x02, vdsMessage.encoded)
    .addMessage(0x80, readBinaryFromResource("face_image_gen.jp2"))
    .addMessage(0x84, "2026-04-23")
    .addMessage(0x86, 0x02)
    .build()

// 3. Build a signed Icao Barcode
val signature = buildSignature(header.encoded + messageGroup.encoded)
val payload = IdbPayload(header, messageGroup, null, signature)
val icb = IcaoBarcode(isSigned = true, isZipped = false, barcodePayload = payload)

// The encoded raw string can now be used to build a datamatrix (or other) code - which is not part of this library
val encodedRawString = icb.rawString

Also have a look at the testcases for more usage inspiration. You will also find an example on how to generate a datamatrix image with the Zxing library in the jvmTests.

Documentation

javadoc

Online JavaDoc can be found here: https://javadoc.io/doc/de.tsenger/vdstools

How to include

The vdstools library is available on the Maven Central Repository and GitHub Packages to be easy to integrate in your projects.

Gradle

To include this library to your Gradle build add this dependency:

dependencies {
    implementation 'de.tsenger:vdstools:0.9.0-SNAPSHOT'
}

Maven

To include this library to your Maven build add this dependency:

<dependency>
    <groupId>de.tsenger</groupId>
    <artifactId>vdstools</artifactId>
    <version>0.9.0-SNAPSHOT</version>
</dependency>