Signing Code With a Code Signing Certificate

Code signing is a great way to detect if an exe or dll has had been modified by someone other than the person that created it. Some systems give slightly higher trust to code that has been signed. I’ve finally gotten my own code signing certificate after a frustrating experience with a software security system generated a security event after I created helloworld.exe to test a compiler (among other issues). The security software tends to be less agressive when there is an atestment for the source of the executable. When

Summary

  • The code signing certificate is stored on a physical secure element.
  • Physical access to the token is needed to sign code. Treat it as any device that contains secure informatio.
  • Individuals probably want an OV code signing certificate. Larger corporations probably want an EV certificate.
  • Code signed with this certificate becomes tied to the ID of the signing organization.

My Selected Certificate Issuer

I selected “Sectigo” as my certificate issuer. Part of the reason for my choice was that when I checked other issuers, I found that they were actually resellers for other issues. The reseller prices were understandably a bit higher than going direcly to an issuer.

Types of Code Signing Certificates

There are two types of code signing certificates. Both certificates are associated with some level of verification. The lower level verification certificates are cheaper and require that less information be verified (This is what I purchase). For this certificate, verification of the name of the person or entity that is requesting the certificate is verified. The ID of the person making the request is verified along with ensuring they are authorized to act on behalf of the entity. There are also phone number and email verification. For, the most difficult part of the verification was the phone number verification. The certificate issuer tries to verify the phone number against public records. But if the phone number isn’t on some public record, they will accept a letter from one’s accountant or lawyer that ties this information to the other factors. I used a legal professional for phone verification.

Extended verification looks at additional factors such as an entity’s DUNs number and demand deposit account. If you don’t have these or are not familiar with what they are, you probably don’t want to go this route. For developers of lower-level applications such as system drivers will want this level of trust though. Windows adds a lot of friction to installing certain types of drivers that are not

Once I completed verification, the key shipped out pretty darn fast. I completed my verification around 12:30pm on one day, and the certificate was delivered to my house by 12:00 the next day.

Physical Token

Part of the security of modern code signing certificates is that they are shipped on a physical device. This is a requirement that Microsoft imposes for security reasons. If the certificate isn’t saved on a drive where someone can get to it, then it can’t be compromised. To get the key, someone would need to steal the physical device.

At first glance, one might confuse this token with a USB storage drive. It’s not. Once I connected it to my computer, it became apparent what my security token is. It is a smart card with a USB interface. I’ve made a couple of post about SmartCards (AKA JavaCards). Among other capabilities, JavaCards are able to store and security certificates and perform encryption and decryption without making the encryption keys themselves visible. They implement anti-tampering logic. Experimenting or attempting to circumvent the security can result in the device being permanently disabled. The token is protected with a couple of passwords. There is an administrator password and a user password. The user password for my token was made available to me via e-mail when the token was shipped. The user password can be changed. The administrative password is never made available to the end user. When connected to the computer, the device manager as a “Smart card reader.”

For Signtool to communicate with the token, an additional software item is necessary. For the token that I am using, this is the “SafeNet Authentication Client.” The download URL for the client was provided from my issuer in the same e-mail in which they shared the password. After installing the software, one of the first actions I took was to change the user password on the token from the random string of characters that had been assigned to it to something I can remember. The user password is necessary every time code is signed.

Signing and Timestamping the Code

For a DLL or an EXE that you wish to sign, you will use signtool.exe The two commands that you might want to remember for it are how to sign code, time stamp code, and verify signing.

Signing code

For signing code, you will invoke the executable with the sign argument. You’ll need to indicate that the SHA256 algorithm is being used (/fd SHA256). Optionally, you might want to communicate to the tool that it can automatically select the most appropriate certificate for signing (/a).

signtool sign /fd SHA256 /a .\helloworld.exe

After the code is signed, you will want to timestamp the code. The timestamp command requires a URL to a time server.

signtool timestamp /tr http://timestamp.digicert.com /td SHA256 .\helloworld.exe

If you want to view the information on a signed executable, within the Windows Explorer the information shows when you right-click on it and select “Properties.” A “Digital Signatures” tab will be present and show the name of the signner and the timestamp.

Windows properties windows showing two additional details paines with information on the certificates used to sign an exe.

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Identifying the Bank that Issued a Card

In exploring aspects of Java Card programming, I’ve been using some of my older bank cards. A question that came to mind is how does one identify the issuer of a card. I didn’t see that information explicitly encoded into the information that I could retrieve from the card. I did find the information. It is implicitly there. It can be inferred from the first few numbers of the account number that the card returns. Generally at least the first 6 numbers are needed to identify the bank. Though there are a few banks that only need 4 digits, and a few that need as much as 8 to identify. These prefixes are called Bank Identification Numbers (BIN).

There are a few reasons why someone might want to identify the card issuer. There have been promotionals from agreements between vendors and banks where the vendor gives a discount or early access to those that purchase with a card from a specific bank. There are also some banks associated with higher rates of charge-backs. One might want to take additional precautions in those instances. Card issuers also tend to be associated with a specific country. A card number could be used for establishing presence within some country.

Inferring the issuer is just a matter of finding the entry in a list of prefixes that matches the prefix of the account number in question. I’ve made two code examples available for doing this. One is written in C#, the other in C++. You can find them both in GitHub at this URL: https://github.com/j2inet/binLookup. For both code examples, I’ve included a list of prefixes and banks in embedded resources. This minimizes the number of files that must be as the executable is moved around. It also prevents a casual user from doing something that may be damaging to the data.

You can take a look at the list itself at https://github.com/j2inet/binLookup/blob/main/CS/BinLookup.net/resources/binlist.csv. I’d suggest searching in this document for the first few numbers of your own account number to see if it is in this list.

I’ve searched, but I’ve not found that there exists a unified updated list of Bank Identification Numbers. The source from which I acquired these said that he found them in the WayBackMachine that attributed the entry to an old, now deleted Wikipedia article. This information is only in furtherance of a code exmple, and is not to be relied upon for anything beyond example.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Compiling OpenSSL for JavaCard on Windows with Visual Studio

I needed to compile OpenSSL on Windows in preparation for some JavaCard work. While OpenSSL can be compiled with a range of compilers, I wanted to specifically use Visual Studio because that is the compiler that is generally available on my machines. In addition to Visual Studio, I also needed a build of perl installed and available in the system path. After cloning OpenSSL and navigating to the root of the repository, the next step is generally to configure the build. For the JavaCard tools, a 32-bit version of OpenSSL is needed. I ran into some problems initially with part of the build process targeting 64-bit architecture. To prevent this from happening, some environment variables can be set to ensure the 32-bit version of the tools is used. Visual Studio provides a batch file for setting these environment variables that we can use. Below is the path at which I found this batch file. For you, it may vary depending on the edition of Visual Studio that you have.

C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat

Open a command terminal and run this batch file. Then you can start the build process. To configure the build process for 32-bit Windows with the options needed for the JavaCard environment, use the following command from the repository root.

perl Configure VC-WIN32 no-asm no-threads enable-weak-ssl-ciphers

If you wanted to make a general build, you could omit most everything after VC-WIN32. For a 64-bit build, use `VC-WIN64A.

Now for the long part. After this next command, if you were planning on making coffee or having a quick bite to eat, a good time is about to present itself. From the root of the repository, run the following command.

nmake

If you come back and find that the build process has terminated with a complaint about mixing 32-bit and 64-bit code, then that means that the system is using the 65-bit version of the tools. This will happen if you forgot to run the batch file that I mentioned earlier. If you would like to run the unit tests for OpenSSL, use the following command.

nmake test

This process also takes a significant amount of time. When it completes, the last step is to install OpenSSL. This command will likely fail unless you open an instance of the Visual Studio command prompt with Administrative priviledges.

nmake install

This command will place OpenSSL within c:\Program Files\OpenSSL. The executables themselves are in c:\Program Files\OpenSSL\bin.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Unresolved external symbol WKPDID_D3DDebugObjectName (LNK2001)

I opened an old Direct3D program and tried recompiling it, only get the error LNK2001 Unresolved external symbol WKPDID_D3DDebugObjectName. This is obviously an error from a definition for an element missing. I checked the source code and I saw that the object of interest was defined in d3dcommon.h. Confusing at first, but I finally realized that for the object to be resolved by the linker I needed to include dxguid.lib in the project. There are a few ways to link to a library. I prefer to explicitly link in source code instead of linking in the project settings. In one of my sources files, I only needed to include the following.

#pragma comment(lib, "dxguid.lib")

I only need this file linked when I am compiling in debug mode. A conditional compilation statement wrapped around this will take care of making it conditionally linked.

#if defined(_DEBUG)
#pragma comment(lib, "dxguid.lib")
#endif

With that change, the program compiles and the error has gone away!

For those curious, the D3D program in question is something I have appended in the C++ Application Base Class project. One day I intend to make a base class for a D3D program base class to go along with the D2D base class. The beginnings of my experimentation for it are within that project.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

C++ Custom Deleters

Some organizations and entities (including the White House) have advised against using C/C++ and use memory safe languages with memory safe features instead. While I can understand the motivation for such encouragement, realistically complete abandonment of the language isn’t practical. Managing low-level resources in other languages can both be cumbersome and doesn’t necessarily insulate someone from resource leaks. There are not always higher-level libraries available for functionality that one wishes to use; they may have to build a library themselves and embrace management of those low level resources. But that said, when writing code in C++, one can use safer approaches to doing so. One approach is to use std::shared_ptr<T> instead of using pointers directly.

Shared pointers implement reference counters and will delete the underlying memory once that reference count reaches zero. This is a feature that is often common in some other high level languages. Instead of using the new and delete commands to allocate and release memory, one could use std::make_shared. For other blocks of data for which you might have manually allocated memory, you could use other standard template library classes, such as using a std::vector instead of an array.

Sometimes a resource in question was allocated by the operating system. It may be up to the developer to manage the release or deletion of the object. These can still be managed with std::shared_ptrs<T> objects. Let’s take a look at a simple program that reads a program into a buffer,

#include <iostream>
#include <Windows.h>


const DWORD64 MAX_FILE_SIZE = 64 * 1024;//64 kilobytes


int main(int argc, char** argv)
{
	if (argc < 2)
	{
		std::wcout << L"Usage: ShowFileContents <filename>" << std::endl;
		return 1;
	}
	std::string filename = argv[1];
	std::wstring wfilename = std::wstring(filename.begin(), filename.end());
	HANDLE hFile = CreateFile(wfilename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
	DWORD fileSizeHigh, fileSizeLow;
	DWORD64 fileSize =  -1;
	DWORD bytesRead = -1;

	fileSizeLow = GetFileSize(hFile, &fileSizeHigh);
	fileSize = ((DWORD64)fileSizeHigh << 32) + fileSizeLow;
	if (fileSize > MAX_FILE_SIZE)
	{
		std::wcout << L"File is too big to read" << std::endl;
		CloseHandle(hFile);
		return 1;
	}
	std::wcout << L"File size: " << fileSize << std::endl;
	char* buffer = new char[fileSize + 1];
	ZeroMemory(buffer, fileSize + 1);
	ReadFile(hFile, buffer, fileSize, &bytesRead, NULL);
	std::wcout << L"File contents: " << std::endl;
	std::wcout << buffer << std::endl;
    delete buffer;
	CloseHandle(hFile);

	return 0;
}

There first thing I see that can be replaced is a call to the new and delete that could be removed. I’ll replace the use of this buffer with a vector<T>. Since I am using a vector, I don’t need to explicitly allocate and deallocate memory. Instead, I can specify how much memory is needed in its declaration. When the std::vector falls out of scope, it will be deallocated automatically. I do make use of a pointer to the vector’s memory. It is accessible through the method std::vector<T>::data(). The ReadFile method needs a pointer to the memory in which it will deposit its data. That’s provided by way of this method.

There is also a HANDLE variable used for managing the file. It is named hFile. I’ve written on wrapping these in unique pointers before. You can read about that here. In that post, I implemented a Functor that contains the definition for how the handle is to be deleted. Rather than manually ensure I associate the functor with the shared pointer, I had also made a function that would handle that for me to ensure it is done the same way every time. This can also be used with a std::shared_ptr<T>. Though you should generally only do this if you really need to share the resource with more than one object. On a unique pointer, the deleter is part of the object type. On a shared pointer, the deleter is not part of the type, but is stored in instance data for the pointer. I’ll replace my usage of CreateFile (the Win32 function) with wrapper function that returns the handle as a std::shared_ptr. That wrapper function looks like this.

using HANDLE_shared_ptr = std::shared_ptr<void>;

HANDLE_shared_ptr CreateFileHandle(
	std::wstring fileName, 
	DWORD dwDesiredAccess = GENERIC_READ, 
	DWORD dwShareMode = FILE_SHARE_READ, 
	LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL, 
	DWORD dwCreationDisposition = OPEN_EXISTING, 
	DWORD dwFlagsAndAttributes = 0, 
	HANDLE hTemplateFile = NULL)
{
	//std::shared_ptr<HANDLE> x = nullptr;
	HANDLE handle = CreateFile(fileName.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
	if (handle == INVALID_HANDLE_VALUE || handle == nullptr)
	{
		return nullptr;
	}
    return 	std::shared_ptr<void>(handle, HANDLECloser());	
}

In the following, you can see the new implementation of my main() method. Notice that in the ReadFile method for the std::shared_ptr<T> that I am calling its get() method to pass the HANDLE value to the function. I’m nolonger explicitly invoking the call to CloseHandle(). Instead, when the main() method returns, the deleter will be invoked indirectly. If you set a breakpoint on it you’ll see when this happens.

int main(int argc, char** argv)
{
	DWORD fileSizeHigh, fileSizeLow;
	DWORD64 fileSize = -1;
	DWORD bytesRead = -1;
	if (argc < 2)
	{
		std::wcout << L"Usage: ShowFileContents <filename>" << std::endl;
		return 1;
	}
	std::string filename = argv[1];
	std::wstring wfilename = std::wstring(filename.begin(), filename.end());

	auto fileHandle =  CreateFileHandle (wfilename.c_str());


	fileSizeLow = GetFileSize(fileHandle.get(), &fileSizeHigh);
	fileSize = ((DWORD64)fileSizeHigh << 32) + fileSizeLow;
	if (fileSize > MAX_FILE_SIZE)
	{
		std::wcout << L"File is too big to read" << std::endl;
		return 1;
	}
	std::wcout << L"File size: " << fileSize << std::endl;
	std::vector<char> buffer(fileSize / sizeof(char) + 1, 0);
	ReadFile(fileHandle.get(), buffer.data(), fileSize, &bytesRead, NULL);
	std::string bufferText = std::string(buffer.begin(), buffer.end());
	std::wcout << L"File contents: " << std::endl;
	std::cout << bufferText << std::endl;

	return 0;
}


You’ll see use of this soon in an upcoming post on SmartCards. The code examples for it make Windows API calls to the Smart Card functions. I’ll be making use of shared pointers with deleters for managing the resources in that project.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

A Quick Introduction to Cosmos DB in C#

This is for those that need to start getting productive within Cosmos DB in a hurry. There’s a lot that could be discussed, but I think you’ll first want to be able to setup a local development environment and get to writing data to it, and reading that data. In this walkthrough, I’ll show how to make a connection to your local/development instance of Cosmos DB. Configuring a production connection is a little more involved. If you are trying to get started, that isn’t an immediate concern. I won’t cover it here. I’ve focused specifically on C# instead of targeting multiple languages to keep this shorter. Let’s get started. You need to first install the Cosmos DB emulator.

Installing the Cosmos DB Emulator

You can download the Cosmo DB Emulator from Microsoft at this address: https://aka.ms/cosmosdb-emulator. You can start the emulator from the command line. For ease of starting it, I would suggest adding the program’s path to your path environment variable. Once installed and the path updated, you can start the Cosmo DB Emulator with the following command.

Microsoft.Azure.Cosmos.Emulator.exe

By default, it will run on port 8081. If you would like to run this on a different port, use the /port=[port number] parameter. Once the emulator is running, you can the contents of the emulator at the URL https://localhost:8081/_explorer/index.html. This view will show you information for connecting to the emulator instance. Note that the information shown here will be the same on every computer on which you run this. The emulator is only for testing and not for production. The emulator accepts communication over TLS. For this purpose, the installation of emulator also resulted in the installation of a development certificate for encrypting the TLS traffic.

Create a new project and either use the package manager to add a reference to Microsoft.Azure.Cosmos. If you are using the command line to manage your project, from your project directory use the following command.

dotnet add package Microsoft.Azure.Cosmos

Creating the Database and Container in C#

With that in place, we can get into the code. start by adding a using statement for the code.

using Microsoft.Azure.Cosmos;

There are a few objects that we need to create. We need to create a client object for connecting to the database, the database object itself, and containers within the database. If you are familiar with traditional databases, then containers are similar to table. When we create the container, the values put in it will have an identifier value in a field named id. We also identify a field on which the data will be logically grouped/partitioned. For my example, I’m using an employee database and am parittioning on the department to which someone is assigned. The partition key identifier is expressed in what looks like a file path. But insted of a path in nested folders, this would be a field in what could be nested objects. If the partition key is at the root of the object, then this path will look like a file path to a file that is in the root directory.

For our local test, we will be using a resource key that is the default for any local instance of the Cosmos DB service. We would **not** be using this in a production environment. But for our local tests, it is fine.

const string RESOURCE_TOKEN = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
using CosmosClient client = new(accountEndpoint: "https://localhost:6061", authKeyOrResourceToken: RESOURCE_TOKEN);
Database database = await client.CreateDatabaseIfNotExistsAsync("employeeDatabase");
Container employeeContainer = await database.CreateContainerIfNotExistsAsync("employeeContainer", "/id");

If we run the above vode and then open our browser to the Cosmos DB explorer, we will find that there’s a new container named employeeContainer. Though, there is no data in it.

Adding an Item to the Container

We could add an item to the table with only one or two more statements of code. To put an object into the container, we can create and initialize an object. Then we UpSert it into the database.

var item = new Employee()
{
    ID = Guid.NewGuid(),
    Name = "Joel Johnson"
};
await employeeContainer.UpsertItemAsync(item);

Now if we run the code and look in the Cosmos DB explorer, we will see our item. In addition to the fields from the public elements of our object, there are some additional fields prefixed with an underscore (_) that have additional metadata on our object, such as the time stamp (_ts) and etag (_etag).

Reading an Item from the Container

If we wanted to retrieve a specific item from the container and we know its id value and partition key value, we can use the ReadItemAsync<T> method on the container to retrieve the item. This method will deserialize the contents and return our data as an object.

var readValue = await employeeContainer.ReadItemAsync<Employee>(
    id: idvalue.ToString(), 
    partitionKey: new PartitionKey("IT"));

We could also read the item as a stream. Readin gthe item this way will result in all the data associated with the item being read, including the fields that have additional metadata.

var readValue = await employeeContainer.ReadItemAsync<Employee>(
    id: idvalue.ToString(), 
    partitionKey: new PartitionKey("IT"));

var itemStream = await employeeContainer.ReadItemStreamAsync(
                    id: idvalue.ToString(), 
                    partitionKey: new PartitionKey("IT")
);
using ( StreamReader readItemStreamReader = new StreamReader(itemStream.Content))
{
    string content = await readItemStreamReader.ReadToEndAsync();
    Console.WriteLine(content);
}

Querying an Item

You probably don’t know the exact ID of the valu(s) that you want to read. But you may know something about other data for items you want. Ironically, while Cosmo DB is a “NoSQL Database”, it supports queryign with SQL.

using  FeedIterator<Employee> feedIterator = employeeContainer.GetItemQueryIterator<Employee>(
                    queryText: "SELECT * FROM c WHERE c.dept = 'IT'");
while(feedIterator.HasMoreResults)
{
    FeedResponse<Employee> response = await feedIterator.ReadNextAsync();
    foreach(Employee employee in response)
    {
        Console.WriteLine($"Found item {employee}");
    }
}

You wouldn’t want your code to be vulnerable to SQL injection attacks. If a parameter could vary, you don’t want to construct a string. You want to pass the value as parameterized input. In the SQL query, named parameters are prefiexed with the @ symbol. In the above, if we wanted to pass the department as a parameter instead of embedding it in the query, we would use code like the following.

QueryDefinition query = new QueryDefinition("SELECT * FROM c WHERE c.dept = @dept")
    .WithParameter("@dept", "IT");
using FeedIterator<Employee> feedIterator = employeeContainer.GetItemQueryIterator<Employee>(query);

If you are familiar with LINQ, you could also use that to query information also. The container’s GetItemLinqQuerable<T>() method returns an object that you can use for LINQ queries.

var employeeLinqContainer = employeeContainer.GetItemLinqQueryable<Employee>(allowSynchronousQueryExecution: true );
var employeeQuery = employeeLinqContainer
    .Where(e => e.Department == "IT")
    .Where(e => e.DateOfBirth > new DateTime(1970, 01, 01))
    .OrderBy(e => e.Name);
            
foreach(var employee in employeeQuery)
{
    Console.WriteLine($"Found item {employee}");
}

I hope that this was enough to get you started!


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Reading Magnetic Card Data

Magnetic cards have been around for a while. It feels only recently in the past few years that point of sale systems in the USA primarily switched to NFC and chipped cards for payments. But magnetic cards still have lots of usages, such as gift cards or door entry cards. While cleaning up, I found a magnetic card reader that was used for a project a long time ago. I happen to find this at a time where I’m already writing about NFC cards, just wrote about the Luhn’s Check algorithm used in credit card entry, and looking at how to read smart cards. I think it’s fitting to take a glance at reading information from magnetic cards. There are multiple standards for encoding information onto magnetic cards.

The physical unit I found was purchased for part of the functionality that was used in a demo for a concept in a past NRF Conference. I still have some video from the event.

This particular reader appears as an HID device to the computer. When a card is scanned, it generates keystrokes to the computer. For demonstration, I am using a gift card from “Raising Cane’s.” I’ve selected this card because I believe the card is cancelled and thus has no funds on it. But even if it does have funds on it and someone uses it, I suffer no lost since I am not positioned to suffer a lost. I also found an old American Express gift card whose funds have long been exhausted.

Encoding

The data on track 1 of the cards is encoded in 7 bits; 6 bits are data, 1 bit is for parity. This means that only 64 possible characters could be in the encoding. This isn’t UTF-8 or ASCII like you may be acustomed to. The reader does translate from these encoding to the equivalent keystrokes. Data on tracks 2 and 3 (if they exists) have 5 bits per character. Track 1 can have up to 79 characters, track 2 up to 40, and track 3 up to 107 characters.

I’ve got a tables at the end of this post that shows the actual encodings and their classifications. The reader will translate these to ASCII encodings for you. These are primarily included as a curiosity. There is more than one standard for encoding data on magnetic cards. With some of the hotel key cards that I have, I found some of them don’t work with this reader. This is somewhat expected, as a hotel might want to use a format that isn’t as easy to replicate. The varied gift cards, membership cards, and payment cards I tried generally worked fine.

Error Response

I want to start off talking about the error response since people often overlook those. If you were to use a card that the reader cannot process, it will return the string +E?. If you receive this response, the reading failed, possibly because the card is using an encoding that isn’t understood.

Sample Card Scan

Here’s the data that comes back when I read the “Raising Cane’s” gift card. I will refer to it in the following sections.

%B6000205500145033524^GIFT/RAISINGCANES^4211?;6000205500145033524=4211101685485?

Start and End Sentinel

When a card is read, the first character in the data will be a %. The last character will be a ?. If you were making something that were reading card data, you could use these characters to let your program know that it is receiving card data or that the data. Fields on the card are separated with the ^. Though not part of the card data, when my specific card reader has finished reading data, it will also send an enter keystroke. All of the data between the % and ? comes from the card. But there are some more delimiters in that block of data.

Track Delimiters and Field Delimiters

A magnetic card could have multiple tracks in parallel. The card reader returns all three tracks in a single stream of data. But the tracks are delimited with a semicolon(;). A single track could have multiple fields of data. Fields are separated with the caret (^) character on track 1 or an equal (=) character on tracks 2 and 3. Parsing out the sample card read I provided above, we end up with the following. The exact purpose of each of these fields could vary by card.

Track 1 Field 1:B6000205500145033524
Track 1 Field 2:GIFT/RAISINGCANES
Track 1 Field 3:4211?
Track 2 Field 1:6000205500145033524
Track 2 Field 2:4211101685485

Card Class

The very first character read on the card indicates the class/type of card being read. For all of the payment cards (both gift cards and credit cards) that I’ve encountered, this character is a ‘B’. From my readings and reading other cards that I have, I have found the following.

PrefixAssociation
BPayment or Gift Card
GGift Card
MMembership Card
GCGift Card
Card class based on the first character

Credit Card Format

While I found the way data is structured on a card to be variable, when I tried several credit cards, the construction of the data is consistent. Here is a modified data stream from a gift card.

%B377936453080000^THANK YOU                 ^2806521190729520                ?;377936453080000=280652119072952000000?

Breaking it out into fields, we have the following.

FieldPurposeData
Track 1 Field 1Primary Account Number377936453080000
Track 1 Field 2NameTHANK YOU
Track 2 Field 3Expiration Date (2028-06)
Service code (521)
Discretionary Data(190729520)
2806521190729520
Track 2 Field 1Credit Card Number377936453080000
Track 2 Field 2Expiration Date (2028-06)
Service code (521)
Discretionary Data (190729520)
280652119072952000000

You’ll notice that some of the data is on the card twice. I’m not quite sure of the reason for this. Is that for verifying the integrity of the data? To provide an alternative method of reading data for cheaper devices, allowing them to only read one track? This, I don’t know.

Reading the Data in Code

As I said the post on Luhn’s Check, don’t enter a real credit card number on the site where I have sample code posted. Though the site doesn’t actually communicate any data back to any site, I think it is still better to advise you not to enter data. But the site is there for you to examine the code. Feel free to download it to run in a local sandbox (where you can prohibit Internet communication) or view the source code to see how it works. You can find the code at https://j2inet.github.io/apps/magreader. If you would like to use a magnetic card reader that is similar to what I have, you can find it here (affiliate link):

Encoding Tables

BCD Data Format

CharacterHexFunction
00x00DATA
10x01DATA
20x02DATA
30x03DATA
40x04DATA
50x05DATA
60x06DATA
70x07DATA
80x08DATA
90x09DATA
:0x0AControl
;0x0BStart Sentinel
<0x0CControl
=0x0DField Separator
>0x0EControl
?0x0FEnd Sentinel

Alpha Encoded Data

CharacterHexFunction
[space]0x00Special
!0x01Special
0x02Special
#0x03Special
$0x04Special
%0x05Start Sentinel
&0x06Special
0x07Special
(0x08Special
)0x09Special
*0x0ASpecial
+0x0BSpecial
0x0CSpecial
0x0DSpecial
.0x0ESpecial
/0x0FSpecial
00x10DATA
10x11DATA
20x12DATA
30x13DATA
40x14DATA
50x15DATA
60x16DATA
70x17DATA
80x18DATA
90x19DATA
:0x1ASpecial
;0x1BSpecial
<0x1CSpecial
=0x1DSpecial
>0x1ESpecial
?0x1FEnd Sentinel
@0x20Special
A0x21DATA
B0x22DATA
C0x23DATA
D0x24DATA
E0x25DATA
F0x26DATA
G0x27DATA
H0x28DATA
I0x29DATA
J0x2ADATA
K0x2BDATA
L0x2CDATA
M0x2DDATA
N0x2EDATA
O0x2FDATA
P0x30DATA
Q0x31DATA
R0x32DATA
S0x33DATA
T0x3fDATA
U0x35DATA
V0x36DATA
W0x37DATA
X0x38DATA
Y0x39DATA
Z0x3ADATA
[0x3BSpecial
\0x3CSpecial
]0x3DSpecial
^0x3EField Separator
_0x3FSpecial

Hardware for Java Card Development

I’ve got a series of posts on JavaCard development. There is some hardware that is needed if you want to deploy your JavaCard programs to physical hardware. The cards that I’m using are both chip cards and dual-interface cards (meaning that they both have the chip and can communicate over NFC). They also have magnetic strips on the back. If you want to try the same hardware, this is a listing of what I’ve got. Note that the links in the following are affiliate links. If you purchase through these links, I earn a small commission that goes towards supportin gthis site and my activities.

Blank Java Cards

Java 3 Dual Interface Cards

These are the cards on which I’ll be running my programs.

The JR150 cards are dual interface cards. They can interact both through the chip and wirelessly, making them accessible to Android phones. These cards use the smaller 6-contact version of the chip interface and a printable in card printers. The two that I received came with their keys on a sticker enclosed in the device. They key values were the same as what was listed on the Amazon Listing as example keys. I’m reposting here, should you get these cards and the if the same key applies. The cards come with at least 99 KB of storage and supports RSA encryption of up to 4096 bits.

CDKenc: 90379Aะ—E7116D455E55F9398736A01CA
CDKmac: 473F36161A7F7F60CC3A766EA4BE5247
CDKkek: D3749ED4FF42FD58B39EEB562B017CD9

Java 2 Chip Interface Cards

These cards *only* have the chip interface and are suitable for Java Card 2 applications. It has 40 kilobytes of storage and supports RSA encryption up to 2048 bits.

Card Readers

Contactless Only Card Reader

This inexpensive card reader/writer has a wireless-only interface. On Windows, no driver installation is needed. The operating system immediately recognizes it and makes it available to your programs.

Contac + Contactless Reader

This card reader cost a bit more than the wireless-only, but is able to read either type of card. Though it will be listed as two card readers by Windows, only one mode can be active at a time. It will either read a contactless card or a contact card, but not both at the same time. Like the previously mentioned card reader, the drives built into windows already works with this device.

Magnetic Reader/Writers

Text data can be encoded on the magnetic strips on the card. Note that the text data uses an encoding that does not make all ASCII characters available. The encoding for english letters doesn’t distinguish between upper and lower case letters. Card readers tend to return letters in upper case lettering.

Reader/Writer

If you want to also write information to the magnetic strip on the back of the card, you will want a card writer. Though, don’t put anything confidential on the magnetic card. Those contents tend to be stored in plain text and would easily be lifted by someone else.

Magnetic Reader

This magnetic reader has no capability for writing to a strip. To the computer, it appears as a keyboard. Swiping a card through it will result in the computer receiving keystrokes that correlate to the data on the card. You’ll need to parse the data out. I’ve written about the data encoding in another post that you can find here.

Card Skins

I checked out card printers, and even the cheapest among them is too expensive for handling a small volume of cards. So that my cards were not bear white, I decorated them using “card skins.” These are marketed as decorations for payment cards. I did add a few of these to a couple of payment cards too. At this point in time, the only information on the front of a card is a name and the name of a bank. Most of the relevant information is on the back of the card. If you get one, you’ll need to know if you are planning to place the skin on a card with the smaller 6-contact chip or the larger 8-contact. There exists an American Express with a 10-contact chip. I’ve not seen it in person and don’t know it’s size. If you have a 10-contact card, be aware that these skins might not fit. These are the cards that appear in my videos or my posts.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

GM Access to Tesla Superchargers by Spring 2024?

Various car manufacturer adopted NACS (Tesla charging interface) as their preferred interface for their cars. Those manufacturers have also said that the cars they’ve already sold which use the CCS interface for charging will be able to acquire an adapter for charging at Tesla super chargers when the network opens. Ford vehicles already have access to the Tesla super charging network. GM had stated that they would have access to the Tesla charging network by Spring 2024. At the time that I’m posting this, 2024 June 12, it is only 8 more days until the first day of summer.

There have been concerns about hitting that date, especially since Tela fired the entire supercharger team. But according to InsideEVs, GM said that it was still on schedule to deliver according to its original deadline. With the first day of summer being only a week away, we will see whether they actually hit that mark.

“We plan to stick to the timeline that we previously announced, and that’s kind of all I can say,” Sanaz Marbley,ย director of global strategic technology communications at GM, toldย InsideEVs.ย 

As this rollout occurs, some, including myself, have wondered what this will mean for Tesla drivers. Newer Tesla chargers have much longer cables (Version 4 chargers). But many of the chargers already available have much shorter cables. These cables were meant to only power Tesla vehicles, which have their charge port in the back driver’s side tail light (Tesla drivers always back into the space). Many other vehicles have their charge port further away somewhere on the side of the vehicle. From the perspective of a Tesla charger, the port may also be on the wrong side. This results in a driver either needing to double-park for the cable to reach or use the charger on the “wrong side” from where they parked. Either situation results in two chargers now being unavailable to a Tesla driver.

Tesla states that drivers of other EVs may use the charger on the “wrong” side, as this is necessary for charging. But they ask that drivers not park diagnally. Though I can’t use the chargers yet, I drove a Chevrolet EUV Bolt to a local bank of version 3 super chargers and found that I could only reach the cable if I parked diagnally.

Interesting times are ahead at the Tesla charging banks.

I had to park diagnally for the cable to reach my car.

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Exhausting the Computer’s Hot Air from my Home

The concept of transporting the heat produced by an appliance to the outdoors isn’t by any means new. Whether it is the hot air rising out of an oven or the exhaust from the dryer, it’s a concept implemented for generations. Less common in the home environment, however, is the heat from a computer. There are probably people mining cryptocurrency that have already tackled this problem. Though I’m not mining, I found myself faced with it when rendering video or playing a graphically intensive video game. I recently decided to do something about it. On the days I am working from home, it could sometimes become uncomfortably hot in my office though it is cool in the rest of the house. I thought it would be unreasonable to further cool the house just to make it more comfortable in one room; that feels like a waste of energy. Instead, I decided to evacuate the hot air from the computer before it spills out into the room.

I feel like there should be a ready-made solution available somewhere. But my searches turned up next to nothing. I did find an ant-miner adapter made for connecting a 4-inch hose to the antminer. This is a must have for what I’m doing! Four inches is the same diameter as the exhaust hoses used on many residential dryer. The computer in question is near a window. I started off looking for the adapters that are used for portable air conditions. These are usually made to better blend-in with the windows. I wasn’t able to find any that would work for my purposes. I did however find an adapter that was made for venting the air from a drive-through a window. This was perfect! It also used the same 4 inch fitting that the antminer adapter uses. The last major piece I needed was something to help move the air along the hose.

The exhaust fan on a computer is usually blowing air out into an open room. When that same airflow is directed through the dryer hose there may be some back-pressure to overcome. To compensate for this, was I initially was going to for an additional computer fan on the ant-miner adapter. When I tested this out with the 12-volt fan, the air flow was weak. I even tried slightly over-volting the fan to 14 volts to little effect. This was going to be inadequate. I was going to need a stronger solution. With a little more searching I discovered that I could also get 4-inch inline duct fans. This was perfect! There are a variety of duct fans available. For myself, I got a single speed fan. Other options to consider are do you want an inline fan to have a power switch on it, do you want it to be adjustable speed, and what flow rate you want it to have. The flow rate, usually expressed in Cubic Feet per Minute, tells what volume of air it can move over a minute. The higher this number, the more air it moves.

The window adapter with the vent in place.

Before installing anything I fit the pieces together and turned it on. It was working great! when I turned it on I could see the shutters on the exhaust vent open a little. The fan, though audible, didn’t sound much different from the quiet sound of air flowing from an AC unit. For the installation, I had to remove the screen from my Window. The screen was designed to be easily removed and restored.

The exhast vent on the computer with magnets placed around it.

I wanted to be able to quickly attach and disconnect the exhaust vent to my computer. I’ve placed four magnets around the area of the back of the computer that the hot air comes from. I also placed four screws on the antminer adapter. When I line up the adapter with the magnets, it stays in place. There is also weather stripping material that came with the window adapter that I’ve placed on the edges of the ant miner adapter so that it better captures the hot air.

Adapter and hose magnetically atached to the computer.

Drawbacks

Aesthetically, though the adapter was designed to better blend in with the window, it doesn’t completely blend in. Having to remove the screen from my window makes it visually different from my other windows. That’s a drawback. The exhaust duct is warm to the touch. This is expected, since there is warm air flowing through it. But this also means that some of the thermal energy is leaking through it into the room. An insulated duct could prevent this. Not that the current state is awful. Keep in mind previously 100% of the thermal energy was being dumped into the room before. As you might imagine, the window fitting does not block the transfer ot noise to/from the room as well as the double paned glass. If I were in a noisy environment, this could be problematic. I’d like a way to exchange the hot air with the outside without actually pushing air out the window. I’m sure the negative pressure that this solution puts on the room results outside air beign pulled into other areas of the house. An idea solution would perform the heat exchange with the outside without pushing out the air. That would be an air-condition.

Items Purchased or Suggested

Below you will find affiliate links to items that I used or that I would recomend that someone else consider if they decide to implement a similar solution for themselves.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Luhn’s Algorithm, The Checksum used on Credit Cards

I was working with hardware that was sequential in coming off a production line. Looking at the hardwares’ serial number, I realized the numbers were mostly sequential, with the exception of the suffix on the serial number. I realized that I was looking at numbers suffixed with a checksum. I tried out a few checksum algorithms to figure out which one fit. This was only being done from curiosity.

One algorithm of special interest was Luhn’s algorithm. This is the algorithm that is used with IMEI, credit card numbers, and Canadian social security numbers. It’s a useful public domain algorithm. I’ve collected a few implementations of it here.

C++

bool pasesLuhnCheck(const string& cardNo)
{
    int nDigits = cardNo.length();
 
    int nSum = 0, isSecond = false;
    for (int i = nDigits - 1; i >= 0; i--) {
 
        int d = cardNo[i] - '0';
 
        if (isSecond == true)
            d = d * 2;
 
        // We add two digits to handle
        // cases that make two digits after
        // doubling
        nSum += d / 10;
        nSum += d % 10;
 
        isSecond = !isSecond;
    }
    return (nSum % 10 == 0);
}

C#

    public static bool CheckLuhnParity(string digits)
    {
        bool isValid = false;

        if (!string.IsNullOrEmpty(digits))
        {
            long sum = 0;
            int parity = digits.Length % 2;
            for (int i = 0; i < digits.Length; i++)
            {
                int digit = digits[^(i + 1)] - '0';
                sum += (i % 2 == parity) ? Luhn(digit) : digit;
            }
            isValid = (sum % 10) == 0;
        }
        return isValid;
    }

Kotlin

fun IsValidLuhnString(entry:String): Boolean {
    var sum = 0
    var isSecond = false
    for( i in entry.length-1..0) {
        val multiplier = if(isSecond) { 2} else {1}
        val digit = entry[i].digitToInt() * multiplier
        sum += digit / 10;
        sum += digit % 10
        isSecond = !isSecond
    }
    return (sum % 10) == 0
}

JavaScript

const luhnCheck =( num, radix) => {
    if(radix == null) { 
        radix = 10
    }
    const arr = `${num}`
      .split('')
      .reverse()
      .map(x => Number.parseInt(x, radix));
    const lastDigit = arr.shift();
    let sum = arr.reduce(
      (acc, val, i) =>
        i % 2 !== 0 ? acc + val : acc + ((val *= 2) > 9 ? val - 9 : val),
      0
    );
    sum += lastDigit;
    return sum % 10 === 0;
  };

You can find a JavaScript/HTML implementation of it at https://j2inet.github.io/apps/luhns. Though this example doesn’t engage in any network communication, while I encourage you to try it out, please don’t use your bank card. Better yet, if you want to examine the code locally, there is also a download link on that page that contains all of the source code.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

MS DOS Source Code on Git Hub

Microsoft has made the MS DOS source code available on GitHub. At the time of this writing, the 1.25, 2.0, and 4.0 versions are available. For those that ever ran it, browsing through it shows some interesting attributes. 85% of the source code is x86 assembler and 13.1% is in C. Typically, programs on MS DOS ran in the same memory space (no surprise, since these versions of MS DOS did not support multi-tasking). In modern development the mechanism that a program used for calling an operating system is abstracted away. With a lot of this source code being written in x86 assembler, the communication between a user program and operating system functions is laid bare. Communication and function calls often occurred through dedicated processor interrupts. Though modern processors still use interrupts (especially for hardware events) much of our communication with modern operating systems don’t have to rely on these. Chances are that you don’t have the equipment to write the compiled images to a disk (do you even have a floppy disk?). But I think it is worth looking through if you ever ran it at any point in your life.


Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Resolving Azurite Error “EPERM operation not permitted”

If you try to invoke the Microsoft Storage Emulator (azurite.exe) and you see the following error, the solution is simple.

C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\Extensions\Microsoft\Azure Storage Emulator>azurite.exe
Azurite Blob service is starting at http://127.0.0.1:10000
Exit due to unhandled error: Error: EPERM: operation not permitted, open 'C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\Extensions\Microsoft\Azure Storage Emulator\__azurite_db_blob__.json~'

The solution is simple. You will likely see this error if your current directory is the same as the directory where the EXE is store. You don’t want to run the program that way. It will result in the program attempting to place its temporary files in that folder. The better way is to ensure that the program’s folder is included in your path variable so that you can invoke it from any folder. The full path to the folder will depend on your edition of Visual Studio. I’m using the Community version. If you are running the Professional version, then replace the folder name “Community” with “Professional” below.

C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\Extensions\Microsoft\Azure Storage Emulator

With it added to your path, the storage emulator will only try to create files in what your current directory is.


Starliner Launch Attempt Scheduled for 17 May 2024

UPDATE: The launch is now delayed until 17 May to give time to replace O2 pressure regulation valve.

Boing is going to make a second attempt to launch its Starliner manned spacecraft. The first attempt was scrubbed because of a stuck valve. The next attempt is scheduled for Thursday 8 May at about 10:11 PM. Emphasis on “Attempt” because there is no guarantee that it will happen. The craft is capable of holding up to 7 people. But for this test flight there will only be three people. Barry Willmore, 61, is a NASA astronaut and former U.S. Navy Captain that will be in command. Sunita Williams, 58, is a former Navy server member, will be piloting. The flight plan for the Starliner includes a stay at the International Space Station for a week before returning the astronauts. If this launches, this will be Boeing’s first manned space flight.

This won’t be the first flight of the spacecraft itself. It first launched in 2019, but failed to rendezvous with the Space Station because of a software error. It was successful in a launch in May 2022. Though after that flight Boeing decided to change the type of tape used on the electrical cables to a material that was more fire resistant since then.

Reading NFC Cards from HTML

Someone had an NFC card for which I needed the data. There are a couple of ways I could have gotten it from him. One way was to ask him to install an app and scan the card. From the app, he could export the data and send it to me. I don’t generally like to ask people to install apps though. The other option was to make a web page and have him use the web page to scan the card. Key factors here is that he was using a Samsung Android phone and he uses Chrome as his primary driver. Chrome on Android has NFC support.

I needed to make the page in a hurry. It’s a single file web page. You can find it at this URL: https://j2inet.github.io/nfcreader/. You can view the source code completely through this link. If you are viewing this article from your computer and want to run the code on your phone, here’s a QR Code to that same URL.

The Interface

The interface is just a bit of HTML decorated with CSS attributes. We don’t need to explore the CSS to understand the interface. There are a couple of buttons for enabling and disabling a scanner, and a checkbox that, when checked, will cause the NFC card’s serial number to also be displayed.

 <div>
       <button id="scanButton" onclick="scanTag()">Scan for Tags</button>
</div>

<div>
    <button id="stopScanButton" onclick="abortScan()" disabled>Abort Scan</button>
</div>

<div style="position:relative;">
    <input type="checkbox" id="showSerialNumber" /> <label for="showSerialNumber">Show Serial Number</label>
</div>        

<div id="scanResult"></div>

<div id="lastError"></div>

How does it work?

At the time of this writing, NFC support is still considered experimental. mozilla.org reports support for Chrome 89, Opera 63, Samsung Internet 15.0, and WebView 89. I only tested this in Samsung Internet, Samsung Internet Beta, and Chrome. I only experienced this working in Chrome. Tho check whether your browser supports this API, see if the window object defines NDEFReader. If it does not, then the device doesn’t support reading NFC. Note that it is possible for a browser to have software support for NFC while the device has no available NFC hardware. That’s not something that you can test for.

if (! ('NDEFReader' in window))  {
   //This device doesn't have NFC capabilities.
   document.getElementById('scanButton').disabled = true;                
   document.getElementById('stopScanButton').disabled = true;
   document.getElementById('lastError').innerText = "Sorry, this app only works on devices capable of reading NFC"
}

Preparing to Read NFC

Before attempting to read the from the NFC card, you’ll need to create an object and set some handlers. You’ll need to create a new NDEFReader object. No parameters are required for its constructor. On the newly created object, add handlers for onreading and onreadingerror.

reader = new NDEFReader();

reader.onreadingerror = (e) => {
     var lastError = document.getElementById('lastError');
     lastError.innerText = e.toString();
}

reader.onreading = (e) => {
   ...
}

We will talk about the body of the onreading method shortly.

Initiating the Scan

You can initiate a scan just by calling the scan() method on the NDEFReader object with no parameters. I don’t suggest that though. You will probably want to pass an abort object to the method. This gives you a method to deactivate the scanner at will. For this purpose, I’ve created an AbortController instance.

ar abortController = new AbortController();

The scan method returns a promise. We can use this to know if something went wrong or if the application decided to terminate the scanning. The object returned in this promose is defined by us. On the object I return I may have an isError and reasonText object.

function scanTag() {
    reader.scan({signal: abortController.signal})
    .then(()=>{})
    .catch((e)=>{
        if(!e) {
             return;
        }
        if(e.isError) {
            console.error(e);
        } else {
            if(e.reasonText) {
            console.info(e.reasonText);
          }
        }
    })
    document.getElementById('scanButton').disabled = true;
    document.getElementById('stopScanButton').disabled = false;
 }

The onreading handler is given an event object. That object has a few elements of concern to us. One is serialNumber, which is a string that contains the serial number for that specific NFC card. The other element is message, which contains a NDEFMessage object. That’s where we will find most of the data! The NDEFMessage object has a field called records; that is a list of the NDEF records written to the card. You may typically encounter NFC cards that only have one message on it, but it can have multiple messages. If you iterate through this object on a non-empty list, you will find one or more NDEFRecord objects. These are the fields from the record that I find to be the most important.

FieldExplination
recordTypeThe type of record. It could be the strings "empty", "text", "url", "smart-poster", "absolute-url", "mine", or "unknown". It could also be a custom domain name and custom type separated with a colon(:).
mediaTypeReturns the mime type of the record.
dataReturns the raw byte data of the record’s payload.
encodingThe encoding used for a text payload. This could be null.

Iterating and Displaying the Records

For displaying the records, I build a few HTML objects and populate their innerText with the values. I optionally include the serial number to the card. I’m only displaying the record type and the text data. For the card that I needed someone to scan, I knew that this would be sufficient for my purposes. I displayed both the text representation of the data and the raw bytes themselves. Displaying the raw bytes was just a matter of converting the byte values to numeric strings. The browser provides a TextDecoder object for this purpose.

utf8decoder = new TextDecoder('utf8');
...
dataTextElement.innerText = utf8decoder.decode(record.data.buffer);

Here is the complete text for what happens when the card is detected.

reader.onreading = (e) => {
    var rootResultElement = document.getElementById("scanResult");                
    rootResultElement.innerText = "";

    var showSerialNumberChecked = document.getElementById('showSerialNumber').checked;
    if(showSerialNumberChecked) {
        var serialNumberElement = document.createElement('div');
        serialNumberElement.class = 'serialNumber';
        serialNumberElement.innerText = e.serialNumber;
        rootResultElement.appendChild(serialNumberElement)                    ;
    }

    for(var i=0;i<e.message.records.length;++i)
    {
        var record = e.message.records[i];
        var envelopeElement = document.createElement('div')
        envelopeElement.className = 'ndfmessage';
                    
        var typeText = document.createElement('div');
        typeText.className = 'recordType'                    
        typeText.innerText = record.recordType;

        var dataElement = document.createElement('div');
        dataElement.className = 'ndefdata'
        dataElement.innerText = bufferToString(record.data.buffer);
        var dataTextElement = document.createElement('div');
        dataTextElement.innerText = utf8decoder.decode(record.data.buffer);
        envelopeElement.appendChild(typeText);
        envelopeElement.appendChild(dataElement);
        envelopeElement.appendChild(dataTextElement);
        rootResultElement.appendChild(envelopeElement);
    }
}

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet