Encryption

The LRS alphapager supports strong encryption to prevent eavesdroppers from reading pages. When an alphapager is not programmed with an encryption key, any encrypted pages received will display a Decrypt error message. When an encryption key is programmed into the alphapager, any unencrypted messages received will display the error message Plaintext rejected.

The pager’s encryption key is set using a command sent to the programming capcode.
Before describing the encryption protocol, the primitives which are used in the encryption protocol are described.

Cipher

The alphapager uses the indirect-key variant of the Noekeon cipher, a block cipher with a 128-bit key and block size. Noekeon is a relatively strong cipher with a low memory footprint and an efficient implementation.

More information on Noekeon, including the specification and reference implementations, can be found at the official Noekeon website.

📘

Note

For brevity, this document will simply use Noekeon when referring to indirect-key Noekeon.

🚧

Warning

Noekeon is actually a family of block ciphers. There are currently two members of the family, the original direct-key cipher and the newer indirect-key variant. Some cryptography libraries, such as libtomcrypt, only provide direct-key Noekeon. If in doubt about which Noekeon variant your cryptography uses, check it against the indirect-key Noekeon test vectors.

Don’t worry if your cryptography library only supports direct-key Noekeon, you can easily build indirect-key Noekeon using direct-key Noekeon. To do this, encrypt the cipher key using an all zero (null) working key. Use the result of this operation as the working key to encrypt the message. See the Noekeon specification for more details.

Noekeon Test Vectors

The test vectors in this section should be used to validate your Noekeon implementation. These are taken directly from the Noekeon reference implementation. All values are in hexadecimal, big-endian byte ordering.

  • Noekeon Test Vector #1
Key = 00000000 00000000 00000000 00000000 

Plaintext = 00000000 00000000 00000000 00000000 

Ciphertext = ba693381 9299c716 99a99f08 f678178b
  • Noekeon Test Vector #2
Key = ffffffff ffffffff ffffffff ffffffff 

Plaintext = ffffffff ffffffff ffffffff ffffffff 

Ciphertext = 52f88a7b 283c1f7b df7b6faa 5011c7d8
  • Noekeon Test Vector #3
Key = ba693381 9299c716 99a99f08 f678178b

Plaintext = 52f88a7b 283c1f7b df7b6faa 5011c7d8 

Ciphertext = 5096f2bf c82ae6e2 d9495515 c277fa70

Cipher Mode

Noekeon is used in conjunction with CTR mode. The counter is 128-bits. The first 64-bits of the counter should contain a nonce, while the last 64-bits contain an integer counter in big-endian format which starts at 0 and increments by 1 for every block.

A possible nonce construction is to use the transmitter’s serial number for 32-bits of the nonce, while the other 32-bits are a counter which is incremented by one after every usage.

📘

Warning

Never reusing a nonce value with the same encryption key is vital for maintaining
the security properties of a cipher in CTR mode. The nonce should be stored in
non-volatile memory so that nonce values are not reused.

Noekeon CTR Mode Test Vectors

The following test vectors should be used to validate an implementation of Noekeon in CTR mode. All values are in hexadecimal, big-endian byte ordering.

  • Noekeon CTR Mode Test Vector #1
Key = 00000000 00000000 00000000 0000000

Nonce = 00000000 00000000

Plaintext = 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 

Ciphertext = ba693381 9299c716 99a99f08 f678178b be2ebec5 b54702cf f14bce9c 9ff6ee1a e1b31e29 9871c31d 0b70baa0 21dc8c1a
  • Noekeon CTR Mode Test Vector #2
Key = ffffffff ffffffff ffffffff ffffffff

Nonce = ffffffff ffffffff

Plaintext = ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff

Ciphertext = d052d4d3 f3da5f15 d399d1b8 46f2c8cd 28c55e39 da6ff251 0d27ef50 c89c3b34 843bea1c ba32bb13 aa4c60ae 7c8b3cea
  • Noekeon CTR Mode Test Vector #3
Key = 3689ef97 1583a71f e2d2ec71 a664c6b5

Nonce = 58489cbb 29da1a33

Plaintext = 54686520 71756963 6b206272 6f776e29 666f7820 6a756d70 6564206f
76657220 74686520 6c617a79 20646f67 2e

Ciphertext = 1892aad4 2ffdfc26 299c0bd5 fadd2416 086fd383 98988b35 9f865004 5ac619d7 9cd91d38 79af4622 29ac8a70 b1

CRC-16

Before being encrypted, a CRC is appended to the plain text. The pager uses this to verify if decryption succeeded. Failures of the CRC check indicate either the incorrect shared key, or that the message is corrupted.

CRC-16, also known as CRC-16-IBM, is used for the CRC. This is a common CRC, which is also used by the USB and SDLC protocols. The relevant parameters for CRC-16 are:

  • Truncated polynomial: 0xa001
  • Initial remainder: 0xffff
  • Final XOR: 0x0000

CRC-16 Test Vectors

Many implementations of CRC-16 are available. The following test vector can be used to validate a CRC-16 implementation. All values are in hexadecimal, big-endian byte order.

Input: 31 32 33 34 35 36 37 38 39 
CRC-16: 4b37

Base64 Encoding

The result of the encryption function is arbitrary 8-bit binary data. Since POCSAG uses
a 7-bit character encoding, base64 is used as a binary-to-text encoding.

Any base64 implementation which is compatible with the PEM (Privacy Enhanced Mail,
RFC 1421) or MIME (Multipurpose Internet Email Extensions, RFC 2045) definitions of base64 may be used with the pager.

Both of these base64 definitions use an alphabet
of ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/,
with = used as a padding character at the end of the stream. The pager does not place any limits on line length, and ignores all line break characters in the base64 encoded stream.

Building an Encrypted Message

Using the primitives described in the preceding sections, the transmitter can build an encrypted message to send to the pager. The procedure is as follows:

Me = Be(N || E(Ku,N,P || C(P))
Mt = "Encrypted\x00\x02" || Me

where:

  • Me = Base64 encoded ciphertext
  • Mt = Final encrypted message
  • || = Concatenation operator
  • Be = Base64 encoding function
  • N = 64-bit nonce value
  • E = Noekeon encryption function in CTR mode
  • Ku = System’s shared encryption key
  • P = Plaintext to encrypt
  • C = CRC16 function in little-endian byte ordering

The "Encrypted\x00\x02" string that is prepended to the beginning of the message indicates that the message is encrypted. The \x00 and \x02 represent ASCII control codes NUL (0x00) and STX (0x02), respectively. Their presence helps prevent confusing a message that happens to start with the word "Encrypted" with an encrypted message. The NUL control code causes many non-LRS POCSAG pagers to simply display Encrypted when they receive an encrypted page.

Example

To tie all the pieces together, a step-by-step example of encrypting a message is presented below. The following parameters are used for this example:

  • Plaintext (P)
The quick brown fox jumped over the lazy dog.
  • Key (Ku)
1a74e17f 26e5ef2e 6d41dcc2 f89622a2
  • Nonce (N)
278ff844 4bedd6c5

The plaintext in hexadecimal format is:

54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a 75 6d 70
65 64 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e

The CRC-16 of the plaintext is abf9. Appending the little-endian representation of the plaintext to the plaintext yields:

54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a 75 6d 70
65 64 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f 67 2e f9 ab

This data is then encrypted using Noekeon in CTR mode using the key and nonce. The result of the encryption function is:

c1 34 a6 2e 1e 03 f5 89 46 cb c4 88 01 2c 7b 08 b0 87 47 89 49 d7 f3 6b
f9 0c d6 fd f7 db ce b7 cd ed c6 4b 79 58 30 e3 9b 41 aa f5 ec a0 ff

The nonce value is prepended to the ciphertext, giving:

27 8f f8 44 4b ed d6 c5 c1 34 a6 2e 1e 03 f5 89 46 cb c4 88 01 2c 7b 08
b0 87 47 89 49 d7 f3 6b f9 0c d6 fd f7 db ce b7 cd ed c6 4b 79 58 30 e3
9b 41 aa f5 ec a0 ff

The nonce plus ciphertext is now base64 encoded. The base64 encoded ciphertext is:

4a 34 2f 34 52 45 76 74 31 73 58 42 4e 4b 59 75 48 67 50 31 69 55 62 4c
78 49 67 42 4c 48 73 49 73 49 64 48 69 55 6e 58 38 32 76 35 44 4e 62 39
39 39 76 4f 74 38 33 74 78 6b 74 35 57 44 44 6a 6d 30 47 71 39 65 79 67
2f 77 3d 3d

📘

Note

It is acceptable to include any form of newline characters in the base64 encoded
ciphertext, although this would waste payload space.

For illustrative purposes, the same base64 encoded ciphertext in ASCII is:

J4/4REvt1sXBNKYuHgP1iUbLxIgBLHsIsIdHiUnX82v5DNb999vOt83txkt5WDDjm0Gq9eyg/w==

The final step is to append the encrypted message prefix, resulting in a final encrypted message of:

45 6e 63 72 79 70 74 65 64 00 02 4a 34 2f 34 52 45 76 74 31 73 58 42 4e
4b 59 75 48 67 50 31 69 55 62 4c 78 49 67 42 4c 48 73 49 73 49 64 48 69
55 6e 58 38 32 76 35 44 4e 62 39 39 39 76 4f 74 38 33 74 78 6b 74 35 57
44 44 6a 6d 30 47 71 39 65 79 67 2f 77 3d 3d

Programming the Encryption Key

The encryption key is programmed into the pager by sending a message to the programming capcode during the pager’s programming window. To program an encryption key, Ku, the following procedure is used.

Ks = Ns(Ku)

where:

  • Ku = User key to be programmed
  • Ns = Noekeon key setup / key expansion function (encrypt Ku with a null (all 0) working key)
  • Ks = Expanded user key

Ks is then formatted as ASCII text into four 32-bit hexadecimal numbers, left padded with 0s to a length of 8 each. This formatted version of Ks is Kf. The following pseudo-code performs the task of creating Kf from Ks.

string Kf;

Kf = sprintf("%08x:%08x:%08x:%08x", 
	(Ks >> 96) & 0xffffffff, 
	(Ks >> 64) & 0xffffffff, 
	(Ks >> 32) & 0xffffffff, 
	(Ks >> 0) & 0xffffffff);

Kf is then converted into base64 encoded ciphertext, Ke, using the same Me construction developed in an earlier section. The working key used for this application of Noekeon is Kp, a key built into the pager’s firmware and used only for the programming encryption keys.

Ke = Be(N || E(Kp,N,Kf || C(Kf))

Finally, Ke is used to construct Mk, the key programming message by prepending [K and appending ].

Mk = "[K" || Ke || ]

Mk is then transmitted to the programming capcode to program the pager’s encryption key. To disable encryption, use the same procedure, but set Ks = ffffffff ffffffff ffffffff ffffffff and then produce Mk.

Example

A step-by-step example of creating an encryption key programming message is now presented. The parameters for this example are:

  • User key to program (Ku) = 63233e70 3ab8dff2 e8c00ed4 5d13f848
  • Nonce (N) = 823ab5bb 91f375c7

Performing the key setup, Ns, on Ku yields Ks = e435dccf 9318e1c4 953a42c1 14b3cc45.

Converting the binary Ks to the textual Kf gives the following:

e435dccf:9318e1c4:953a42c1:14b3cc45

In hexadecimal, Kf is:

65 34 33 35 64 63 63 66 3a 39 33 31 38 65 31 63 34 3a 39 35 33 61 34 32
63 31 3a 31 34 62 33 63 63 34 35

Ke is now calculated from Kf. The CRC-16 of Kf is 47c0. Appending the CRC-16 in little-endian byte order yields:

65 34 33 35 64 63 63 66 3a 39 33 31 38 65 31 63 34 3a 39 35 33 61 34 32
63 31 3a 31 34 62 33 63 63 34 35 c0 47

Encrypting the concatenation of Kf and its CRC-16 using the programming key, Kp, and the nonce yields:

c6 14 9f b4 75 04 36 bb 10 5e ec da 09 22 6a f1 5a 6d 33 
96 20 b3 0d 07 be 7d 40 95 5f da 05 29 c8 ef 25 ed 7a

The nonce is now prepended:

82 3a b5 bb 91 f3 75 c7 c6 14 9f b4 75 04 36 bb 10 5e ec 
da 09 22 6a f1 5a 6d 33 96 20 b3 0d 07 be 7d 40 95 5f da 
05 29 c8 ef 25 ed 7a

Finally, the base64 encoding is performed, yielding Ke:

67 6a 71 31 75 35 48 7a 64 63 66 47 46 4a 2b 30 64 51 51 
32 75 78 42 65 37 4e 6f 4a 49 6d 72 78 57 6d 30 7a 6c 69 
43 7a 44 51 65 2b 66 55 43 56 58 39 6f 46 4b 63 6a 76 4a 
65 31 36 0a

As ASCII text, Ke is:

gjq1u5HzdcfGFJ+0dQQ2uxBe7NoJImrxWm0zliCzDQe+fUCVX9oFKcjvJe16

Finally, Mk is constructed:

[Kgjq1u5HzdcfGFJ+0dQQ2uxBe7NoJImrxWm0zliCzDQe+fUCVX9oFKcjvJe16]

Mk is now transmitted to the programming capcode.