2 * The MIT License (MIT)
4 * Copyright (c) 2024 by Henry Gabryjelski
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 #if !defined(ID48_H__)
28 // This file defines only the structs and API surface.
29 // There are no dependencies on any external code.
34 #define ASSERT(x) ((void)0)
35 #elif defined(ID48_NO_STDIO)
36 #define ASSERT(x) ((void)0)
37 #else // neither NDEBUG nor ID48_NO_STDIO defined
40 #define ASSERT(x) assert((x))
44 #if defined(__cplusplus)
49 /// [0..11] stores K₉₅..K₀₀
52 /// Big-endian, "native" bit order, when viewed linearly from k[ 0..11]
53 /// Mapping to the indices used in the research paper:
54 /// k[ 0] :== K₉₅..K₈₈
55 /// k[ 1] :== K₈₇..K₈₀
56 /// k[ 2] :== K₇₉..K₇₂
57 /// k[ 3] :== K₇₁..K₆₄
58 /// k[ 4] :== K₆₃..K₅₆
59 /// k[ 5] :== K₅₅..K₄₈
60 /// k[ 6] :== K₄₇..K₄₀
61 /// k[ 7] :== K₃₉..K₃₂
62 /// k[ 8] :== K₃₁..K₂₄
63 /// k[ 9] :== K₂₃..K₁₆
64 /// k[10] :== K₁₅..K₀₈
65 /// k[11] :== K₀₇..K₀₀
67 typedef struct _ID48LIB_KEY
{ // 96-bit
71 /// [0..6] stores N₅₅..N₀₀
74 /// Big-endian, "native" bit order, when viewed linearly from rn[0..6]
75 /// Mapping to the indices used in the research paper:
76 /// rn[ 0] :== N₅₅..N₄₈
77 /// rn[ 1] :== N₄₇..N₄₀
78 /// rn[ 2] :== N₃₉..N₃₂
79 /// rn[ 3] :== N₃₁..N₂₄
80 /// rn[ 4] :== N₂₃..N₁₆
81 /// rn[ 5] :== N₁₅..N₀₈
82 /// rn[ 6] :== N₀₇..N₀₀
84 typedef struct _ID48LIB_NONCE
{ // 56-bit
88 /// [0..3] stores O₀₀..O₂₇ 0000
91 /// Big-endian, "bitstream" bit order, when viewed linearly from frn[0..3]
92 /// This is the order in which the research paper typically lists the bits.
93 /// Mapping to the indices used in the research paper,
94 /// where ( O₀₀ .. O₂₇ ) :== output(s₀₇,k₃₂..k₀₅ )
96 /// frn[ 0] :== O₀₀..O₀₇
97 /// frn[ 1] :== O₀₈..O₁₅
98 /// frn[ 2] :== O₁₆..O₂₃
99 /// frn[ 3] :== O₂₄..O₂₇ 0000
101 typedef struct _ID48LIB_FRN
{
105 /// [0..3] stores O₂₈..O₄₇ (12x 0)
108 /// Native format if viewed linearly from frn[0..6].
109 /// Mapping to the indices used in the research paper,
110 /// where ( O₂₈ .. O₅₅ ) :== output( s₃₅, k₀₄..k₀₀ (15x 0) ) == grn
113 /// rn[ 0] :== O₂₈ .. O₃₅
114 /// rn[ 1] :== O₃₆ .. O₄₃
115 /// rn[ 2] :== O₄₄..O₄₇ 0000
116 /// rn[ 3] :== 0000 0000
118 typedef struct _ID48LIB_GRN
{
123 /// When provided a key and nonce, will calculate
124 /// the frn and grn values and store in caller-provided
125 /// output parameters.
128 /// Note: In C++, each parameter would be a reference (not pointer).
130 void id48lib_generator(
131 const ID48LIB_KEY
*key_96bit
,
132 const ID48LIB_NONCE
*nonce_56bit
,
133 ID48LIB_FRN
*frn28_out
,
134 ID48LIB_GRN
*grn20_out
138 /// Initializes to allow iterative recovery
139 /// of multiple potential keys. After calling
140 /// this init() function, can repeatedly call
141 /// the next() function until it returns false
142 /// to obtain all potential keys.
144 /// <param name="input_partial_key">
145 /// Top 48 bits of the key, such as those discovered
146 /// using the proxmark3 command `lf em 4x70 brute`.
147 /// Only k[0..5] are used from this parameter,
148 /// corresponding to K₉₅..K₄₈.
150 /// <param name="input_nonce">
152 /// Typically from a sniffed authentication.
154 /// <param name="input_frn">
155 /// The challenge sent from the reader (e.g., car)
156 /// to the tag (e.g., key).
157 /// Typically from a sniffed authentication.
159 /// <param name="input_grn">
160 /// The response sent from the tag (e.g., key)
161 /// to the car (e.g., car).
162 /// Typically from a sniffed authentication.
165 /// Note: In C++, each parameter would be a reference (not pointer).
167 void id48lib_key_recovery_init(
168 const ID48LIB_KEY
*input_partial_key
,
169 const ID48LIB_NONCE
*input_nonce
,
170 const ID48LIB_FRN
*input_frn
,
171 const ID48LIB_GRN
*input_grn
174 /// This can be repeated called (after calling init())
175 /// to find the next potential key for the given
176 /// partial key + nonce + frn + grn values.
177 /// I've seen combinations that have up to six
178 /// potential keys available, although typically
179 /// there are 1-3 results.
180 /// Each call to this function will return a single
181 /// value. Call repeatedly until the function returns
182 /// false to get all potential keys.
184 /// <param name="potential_key_output">
185 /// When the function returns true, this caller-provided
186 /// value will be filled with the 96-bit key that, when
187 /// programmed to the tag, should authenticate against
188 /// the nonce+frn values, with tag returning the grn value.
191 /// true when another potential key has been found.
192 /// false if no additional potential keys have been found.
195 /// Note: In C++, each parameter would be a reference (not pointer).
197 bool id48lib_key_recovery_next(
198 ID48LIB_KEY
*potential_key_output
201 #if defined(__cplusplus)
205 #endif // !defined(ID48_H__)