/** * The MIT License (MIT) * * Copyright (c) 2024 by Henry Gabryjelski * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #if !defined(ID48_H__) #define ID48_H__ // This file defines only the structs and API surface. // There are no dependencies on any external code. #include #include #include #if defined(NDEBUG) #define ASSERT(x) ((void)0) #elif defined(ID48_NO_STDIO) #define ASSERT(x) ((void)0) #else // neither NDEBUG nor ID48_NO_STDIO defined #include #include #define ASSERT(x) assert((x)) #endif #if defined(__cplusplus) extern "C" { #endif /// /// [0..11] stores K₉₅..K₀₀ /// /// /// Big-endian, "native" bit order, when viewed linearly from k[ 0..11] /// Mapping to the indices used in the research paper: /// k[ 0] :== K₉₅..K₈₈ /// k[ 1] :== K₈₇..K₈₀ /// k[ 2] :== K₇₉..K₇₂ /// k[ 3] :== K₇₁..K₆₄ /// k[ 4] :== K₆₃..K₅₆ /// k[ 5] :== K₅₅..K₄₈ /// k[ 6] :== K₄₇..K₄₀ /// k[ 7] :== K₃₉..K₃₂ /// k[ 8] :== K₃₁..K₂₄ /// k[ 9] :== K₂₃..K₁₆ /// k[10] :== K₁₅..K₀₈ /// k[11] :== K₀₇..K₀₀ /// typedef struct _ID48LIB_KEY { // 96-bit uint8_t k[12]; } ID48LIB_KEY; /// /// [0..6] stores N₅₅..N₀₀ /// /// /// Big-endian, "native" bit order, when viewed linearly from rn[0..6] /// Mapping to the indices used in the research paper: /// rn[ 0] :== N₅₅..N₄₈ /// rn[ 1] :== N₄₇..N₄₀ /// rn[ 2] :== N₃₉..N₃₂ /// rn[ 3] :== N₃₁..N₂₄ /// rn[ 4] :== N₂₃..N₁₆ /// rn[ 5] :== N₁₅..N₀₈ /// rn[ 6] :== N₀₇..N₀₀ /// typedef struct _ID48LIB_NONCE { // 56-bit uint8_t rn[7]; } ID48LIB_NONCE; /// /// [0..3] stores O₀₀..O₂₇ 0000 /// /// /// Big-endian, "bitstream" bit order, when viewed linearly from frn[0..3] /// This is the order in which the research paper typically lists the bits. /// Mapping to the indices used in the research paper, /// where ( O₀₀ .. O₂₇ ) :== output(s₀₇,k₃₂..k₀₅ ) /// then: /// frn[ 0] :== O₀₀..O₀₇ /// frn[ 1] :== O₀₈..O₁₅ /// frn[ 2] :== O₁₆..O₂₃ /// frn[ 3] :== O₂₄..O₂₇ 0000 /// typedef struct _ID48LIB_FRN { uint8_t frn[4]; } ID48LIB_FRN; /// /// [0..3] stores O₂₈..O₄₇ (12x 0) /// /// /// Native format if viewed linearly from frn[0..6]. /// Mapping to the indices used in the research paper, /// where ( O₂₈ .. O₅₅ ) :== output( s₃₅, k₀₄..k₀₀ (15x 0) ) == grn /// /// then: /// rn[ 0] :== O₂₈ .. O₃₅ /// rn[ 1] :== O₃₆ .. O₄₃ /// rn[ 2] :== O₄₄..O₄₇ 0000 /// rn[ 3] :== 0000 0000 /// typedef struct _ID48LIB_GRN { uint8_t grn[3]; } ID48LIB_GRN; /// /// When provided a key and nonce, will calculate /// the frn and grn values and store in caller-provided /// output parameters. /// /// key in 96 bits /// nonce in 56 bits /// /// Note: In C++, each parameter would be a reference (not pointer). /// void id48lib_generator( const ID48LIB_KEY *k, const ID48LIB_NONCE *n, ID48LIB_FRN *frn28_out, ID48LIB_GRN *grn20_out ); /// /// Initializes to allow iterative recovery /// of multiple potential keys. After calling /// this init() function, can repeatedly call /// the next() function until it returns false /// to obtain all potential keys. /// /// /// Top 48 bits of the key, such as those discovered /// using the proxmark3 command `lf em 4x70 brute`. /// Only k[0..5] are used from this parameter, /// corresponding to K₉₅..K₄₈. /// /// /// The nonce value. /// Typically from a sniffed authentication. /// /// /// The challenge sent from the reader (e.g., car) /// to the tag (e.g., key). /// Typically from a sniffed authentication. /// /// /// The response sent from the tag (e.g., key) /// to the car (e.g., car). /// Typically from a sniffed authentication. /// /// /// Note: In C++, each parameter would be a reference (not pointer). /// void id48lib_key_recovery_init( const ID48LIB_KEY *input_partial_key, const ID48LIB_NONCE *input_nonce, const ID48LIB_FRN *input_frn, const ID48LIB_GRN *input_grn ); /// /// This can be repeated called (after calling init()) /// to find the next potential key for the given /// partial key + nonce + frn + grn values. /// I've seen combinations that have up to six /// potential keys available, although typically /// there are 1-3 results. /// Each call to this function will return a single /// value. Call repeatedly until the function returns /// false to get all potential keys. /// /// /// When the function returns true, this caller-provided /// value will be filled with the 96-bit key that, when /// programmed to the tag, should authenticate against /// the nonce+frn values, with tag returning the grn value. /// /// /// true when another potential key has been found. /// false if no additional potential keys have been found. /// /// /// Note: In C++, each parameter would be a reference (not pointer). /// bool id48lib_key_recovery_next( ID48LIB_KEY *potential_key_output ); #if defined(__cplusplus) } #endif #endif // !defined(ID48_H__)