//----------------------------------------------------------------------------- // Copyright (C) 2018 Merlok // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- // Tools for work with COSE (CBOR Object Signing and Encryption) rfc8152 // https://tools.ietf.org/html/rfc8152 //----------------------------------------------------------------------------- // #include "cose.h" #include #include "cbortools.h" #include "util.h" static const char COSEEmptyStr[] = ""; typedef struct { int Value; char *Name; char *Description; } COSEValueNameDesc_t; typedef struct { int Value; char *Type; char *Name; char *Description; } COSEValueTypeNameDesc_t; // kty - Key Type Values COSEValueNameDesc_t COSEKeyTypeValueDesc[] = { {0, "Reserved", "Reserved"}, {1, "OKP", "Octet Key Pair"}, {2, "EC2", "Elliptic Curve Key w/ x- and y-coordinate pair"}, {4, "Symmetric", "Symmetric Key"}, }; COSEValueNameDesc_t *GetCOSEktyElm(int id) { for (int i = 0; i < ARRAYLEN(COSEKeyTypeValueDesc); i++) if (COSEKeyTypeValueDesc[i].Value == id) return &COSEKeyTypeValueDesc[i]; return NULL; } const char *GetCOSEktyDescription(int id) { COSEValueNameDesc_t *elm = GetCOSEktyElm(id); if (elm) return elm->Description; return COSEEmptyStr; } // keys COSEValueTypeNameDesc_t COSECurvesDesc[] = { {1, "EC2", "P-256", "NIST P-256 also known as secp256r1"}, {2, "EC2", "P-384", "NIST P-384 also known as secp384r1"}, {3, "EC2", "P-521", "NIST P-521 also known as secp521r1"}, {4, "OKP", "X25519", "X25519 for use w/ ECDH only"}, {5, "OKP", "X448", "X448 for use w/ ECDH only"}, {6, "OKP", "Ed25519", "Ed25519 for use w/ EdDSA only"}, {7, "OKP", "Ed448", "Ed448 for use w/ EdDSA only"}, }; COSEValueTypeNameDesc_t *GetCOSECurveElm(int id) { for (int i = 0; i < ARRAYLEN(COSECurvesDesc); i++) if (COSECurvesDesc[i].Value == id) return &COSECurvesDesc[i]; return NULL; } const char *GetCOSECurveDescription(int id) { COSEValueTypeNameDesc_t *elm = GetCOSECurveElm(id); if (elm) return elm->Description; return COSEEmptyStr; } // RFC8152 https://www.iana.org/assignments/cose/cose.xhtml#algorithms COSEValueNameDesc_t COSEAlg[] = { {-65536, "Unassigned", "Unassigned"}, {-65535, "RS1", "RSASSA-PKCS1-v1_5 w/ SHA-1"}, {-259, "RS512", "RSASSA-PKCS1-v1_5 w/ SHA-512"}, {-258, "RS384", "RSASSA-PKCS1-v1_5 w/ SHA-384"}, {-257, "RS256", "RSASSA-PKCS1-v1_5 w/ SHA-256"}, {-42, "RSAES-OAEP w/ SHA-512", "RSAES-OAEP w/ SHA-512"}, {-41, "RSAES-OAEP w/ SHA-256", "RSAES-OAEP w/ SHA-256"}, {-40, "RSAES-OAEP w/ RFC 8017 def param", "RSAES-OAEP w/ SHA-1"}, {-39, "PS512", "RSASSA-PSS w/ SHA-512"}, {-38, "PS384", "RSASSA-PSS w/ SHA-384"}, {-37, "PS256", "RSASSA-PSS w/ SHA-256"}, {-36, "ES512", "ECDSA w/ SHA-512"}, {-35, "ES384", "ECDSA w/ SHA-384"}, {-34, "ECDH-SS + A256KW", "ECDH SS w/ Concat KDF and AES Key Wrap w/ 256-bit key"}, {-33, "ECDH-SS + A192KW", "ECDH SS w/ Concat KDF and AES Key Wrap w/ 192-bit key"}, {-32, "ECDH-SS + A128KW", "ECDH SS w/ Concat KDF and AES Key Wrap w/ 128-bit key"}, {-31, "ECDH-ES + A256KW", "ECDH ES w/ Concat KDF and AES Key Wrap w/ 256-bit key"}, {-30, "ECDH-ES + A192KW", "ECDH ES w/ Concat KDF and AES Key Wrap w/ 192-bit key"}, {-29, "ECDH-ES + A128KW", "ECDH ES w/ Concat KDF and AES Key Wrap w/ 128-bit key"}, {-28, "ECDH-SS + HKDF-512", "ECDH SS w/ HKDF - generate key directly"}, {-27, "ECDH-SS + HKDF-256", "ECDH SS w/ HKDF - generate key directly"}, {-26, "ECDH-ES + HKDF-512", "ECDH ES w/ HKDF - generate key directly"}, {-25, "ECDH-ES + HKDF-256", "ECDH ES w/ HKDF - generate key directly"}, {-13, "direct+HKDF-AES-256", "Shared secret w/ AES-MAC 256-bit key"}, {-12, "direct+HKDF-AES-128", "Shared secret w/ AES-MAC 128-bit key"}, {-11, "direct+HKDF-SHA-512", "Shared secret w/ HKDF and SHA-512"}, {-10, "direct+HKDF-SHA-256", "Shared secret w/ HKDF and SHA-256"}, {-8, "EdDSA", "EdDSA"}, {-7, "ES256", "ECDSA w/ SHA-256"}, {-6, "direct", "Direct use of CEK"}, {-5, "A256KW", "AES Key Wrap w/ 256-bit key"}, {-4, "A192KW", "AES Key Wrap w/ 192-bit key"}, {-3, "A128KW", "AES Key Wrap w/ 128-bit key"}, {0, "Reserved", "Reserved"}, {1, "A128GCM", "AES-GCM mode w/ 128-bit key, 128-bit tag"}, {2, "A192GCM", "AES-GCM mode w/ 192-bit key, 128-bit tag"}, {3, "A256GCM", "AES-GCM mode w/ 256-bit key, 128-bit tag"}, {4, "HMAC 256/64", "HMAC w/ SHA-256 truncated to 64 bits"}, {5, "HMAC 256/256", "HMAC w/ SHA-256"}, {6, "HMAC 384/384", "HMAC w/ SHA-384"}, {7, "HMAC 512/512", "HMAC w/ SHA-512"}, {10, "AES-CCM-16-64-128", "AES-CCM mode 128-bit key, 64-bit tag, 13-byte nonce"}, {11, "AES-CCM-16-64-256", "AES-CCM mode 256-bit key, 64-bit tag, 13-byte nonce"}, {12, "AES-CCM-64-64-128", "AES-CCM mode 128-bit key, 64-bit tag, 7-byte nonce"}, {13, "AES-CCM-64-64-256", "AES-CCM mode 256-bit key, 64-bit tag, 7-byte nonce"}, {14, "AES-MAC 128/64", "AES-MAC 128-bit key, 64-bit tag"}, {15, "AES-MAC 256/64", "AES-MAC 256-bit key, 64-bit tag"}, {24, "ChaCha20/Poly1305", "ChaCha20/Poly1305 w/ 256-bit key, 128-bit tag"}, {25, "AES-MAC 128/128", "AES-MAC 128-bit key, 128-bit tag"}, {26, "AES-MAC 256/128", "AES-MAC 256-bit key, 128-bit tag"}, {30, "AES-CCM-16-128-128", "AES-CCM mode 128-bit key, 128-bit tag, 13-byte nonce"}, {31, "AES-CCM-16-128-256", "AES-CCM mode 256-bit key, 128-bit tag, 13-byte nonce"}, {32, "AES-CCM-64-128-128", "AES-CCM mode 128-bit key, 128-bit tag, 7-byte nonce"}, {33, "AES-CCM-64-128-256", "AES-CCM mode 256-bit key, 128-bit tag, 7-byte nonce"} }; COSEValueNameDesc_t *GetCOSEAlgElm(int id) { for (int i = 0; i < ARRAYLEN(COSEAlg); i++) if (COSEAlg[i].Value == id) return &COSEAlg[i]; return NULL; } const char *GetCOSEAlgName(int id) { COSEValueNameDesc_t *elm = GetCOSEAlgElm(id); if (elm) return elm->Name; return COSEEmptyStr; } const char *GetCOSEAlgDescription(int id) { COSEValueNameDesc_t *elm = GetCOSEAlgElm(id); if (elm) return elm->Description; return COSEEmptyStr; } int COSEGetECDSAKey(uint8_t *data, size_t datalen, bool verbose, uint8_t *public_key) { CborParser parser; CborValue map; int64_t i64; size_t len; if (verbose) PrintAndLog("----------- CBOR decode ----------------"); // kty int res = CborMapGetKeyById(&parser, &map, data, datalen, 1); if (!res) { cbor_value_get_int64(&map, &i64); if (verbose) PrintAndLog("kty [%lld] %s", (long long)i64, GetCOSEktyDescription(i64)); if (i64 != 2) PrintAndLog("ERROR: kty must be 2."); } // algorithm res = CborMapGetKeyById(&parser, &map, data, datalen, 3); if (!res) { cbor_value_get_int64(&map, &i64); if (verbose) PrintAndLog("algorithm [%lld] %s", (long long)i64, GetCOSEAlgDescription(i64)); if (i64 != -7) PrintAndLog("ERROR: algorithm must be -7."); } // curve res = CborMapGetKeyById(&parser, &map, data, datalen, -1); if (!res) { cbor_value_get_int64(&map, &i64); if (verbose) PrintAndLog("curve [%lld] %s", (long long)i64, GetCOSECurveDescription(i64)); if (i64 != 1) PrintAndLog("ERROR: curve must be 1."); } // plain key public_key[0] = 0x04; // x - coordinate res = CborMapGetKeyById(&parser, &map, data, datalen, -2); if (!res) { res = CborGetBinStringValue(&map, &public_key[1], 32, &len); cbor_check(res); if (verbose) PrintAndLog("x - coordinate [%d]: %s", len, sprint_hex(&public_key[1], 32)); if (len != 32) PrintAndLog("ERROR: x - coordinate length must be 32."); } // y - coordinate res = CborMapGetKeyById(&parser, &map, data, datalen, -3); if (!res) { res = CborGetBinStringValue(&map, &public_key[33], 32, &len); cbor_check(res); if (verbose) PrintAndLog("y - coordinate [%d]: %s", len, sprint_hex(&public_key[33], 32)); if (len != 32) PrintAndLog("ERROR: y - coordinate length must be 32."); } // d - private key uint8_t private_key[128] = {0}; res = CborMapGetKeyById(&parser, &map, data, datalen, -4); if (!res) { res = CborGetBinStringValue(&map, private_key, sizeof(private_key), &len); cbor_check(res); if (verbose) PrintAndLog("d - private key [%d]: %s", len, sprint_hex(private_key, len)); } if (verbose) PrintAndLog("----------- CBOR decode ----------------"); return 0; }