25static const uint8_t SMB2_PROTOCOL_ID[4] = {0xFE,
'S',
'M',
'B'};
27size_t pc_smb2_transport_frame(uint8_t *out,
size_t cap,
const uint8_t *msg,
size_t msg_len)
29 if (!out || !msg || msg_len > 0x00FFFFFF || 4 + msg_len > cap)
34 out[1] = (uint8_t)(msg_len >> 16);
35 out[2] = (uint8_t)(msg_len >> 8);
36 out[3] = (uint8_t)(msg_len);
37 memcpy(out + 4, msg, msg_len);
41uint32_t pc_smb2_transport_len(
const uint8_t *buf,
size_t len)
43 if (!buf || len < 4 || buf[0] != 0x00)
47 return ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
50size_t pc_smb2_build_header(uint8_t *buf,
size_t cap, Smb2Command command, uint16_t credit_request, uint64_t message_id,
51 uint32_t tree_id, uint64_t session_id)
53 if (!buf || cap < PC_SMB2_HEADER_SIZE)
57 memset(buf, 0, PC_SMB2_HEADER_SIZE);
58 memcpy(buf + 0, SMB2_PROTOCOL_ID, 4);
69 return PC_SMB2_HEADER_SIZE;
72bool pc_smb2_parse_header(
const uint8_t *buf,
size_t len, Smb2Header *out)
74 if (!buf || !out || len < PC_SMB2_HEADER_SIZE)
78 if (memcmp(buf, SMB2_PROTOCOL_ID, 4) != 0 ||
pc_rd16le(buf + 4) != 64)
83 out->command = (Smb2Command)
pc_rd16le(buf + 12);
84 out->credit_response =
pc_rd16le(buf + 14);
92size_t pc_smb2_build_negotiate(uint8_t *buf,
size_t cap,
const uint8_t client_guid[16], uint16_t security_mode)
94 static const Smb2Dialect dialects[] = {Smb2Dialect::SMB2_DIALECT_0202, Smb2Dialect::SMB2_DIALECT_0210,
95 Smb2Dialect::SMB2_DIALECT_0300, Smb2Dialect::SMB2_DIALECT_0302};
96 const uint16_t ndialects = (uint16_t)(
sizeof(dialects) /
sizeof(dialects[0]));
97 const size_t total = PC_SMB2_HEADER_SIZE + 36 + (size_t)ndialects * 2;
98 if (!buf || !client_guid || cap < total)
104 if (pc_smb2_build_header(buf, cap, Smb2Command::SMB2_NEGOTIATE, 1, 0, 0, 0) == 0)
110 uint8_t *b = buf + PC_SMB2_HEADER_SIZE;
116 memcpy(b + 12, client_guid, 16);
118 for (uint16_t i = 0; i < ndialects; i++)
120 pc_wr16le(b + 36 + i * 2, (uint16_t)dialects[i]);
125bool pc_smb2_parse_negotiate_response(
const uint8_t *msg,
size_t len, Smb2NegotiateResp *out)
132 if (!pc_smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_NEGOTIATE)
137 if (len < PC_SMB2_HEADER_SIZE + 64)
141 const uint8_t *b = msg + PC_SMB2_HEADER_SIZE;
149 memcpy(out->server_guid, b + 8, 16);
159 out->sec_buf =
nullptr;
160 out->sec_buf_len = 0;
163 if ((
size_t)sec_off + sec_len > len || sec_off < PC_SMB2_HEADER_SIZE)
167 out->sec_buf = msg + sec_off;
168 out->sec_buf_len = sec_len;
172size_t pc_smb2_build_negotiate_311(uint8_t *buf,
size_t cap,
const uint8_t client_guid[16], uint16_t security_mode,
173 const uint8_t *salt,
size_t salt_len,
const uint16_t *ciphers,
size_t cipher_count)
175 static const Smb2Dialect dialects[] = {Smb2Dialect::SMB2_DIALECT_0202, Smb2Dialect::SMB2_DIALECT_0210,
176 Smb2Dialect::SMB2_DIALECT_0300, Smb2Dialect::SMB2_DIALECT_0302,
177 Smb2Dialect::SMB2_DIALECT_0311};
178 const uint16_t ndialects = (uint16_t)(
sizeof(dialects) /
sizeof(dialects[0]));
179 if (!buf || !client_guid || !salt || salt_len == 0 || salt_len > 0xFFFF ||
180 cipher_count > PC_SMB2_MAX_OFFER_CIPHERS || (cipher_count > 0 && !ciphers))
187 const bool offer_enc = cipher_count > 0;
188 const size_t body_end = PC_SMB2_HEADER_SIZE + 36 + (size_t)ndialects * 2;
189 const size_t ctx_start = (body_end + 7) & ~(
size_t)7;
190 const size_t preauth_data = 6 + salt_len;
191 const size_t preauth_ctx = 8 + preauth_data;
192 const size_t after_preauth = ctx_start + preauth_ctx;
193 const size_t preauth_pad = ((after_preauth + 7) & ~(
size_t)7) - after_preauth;
194 const size_t sign_ctx = 8 + 4;
195 const size_t after_sign = ctx_start + preauth_ctx + preauth_pad + sign_ctx;
197 const size_t sign_pad = offer_enc ? (((after_sign + 7) & ~(
size_t)7) - after_sign) : 0;
198 const size_t enc_ctx = offer_enc ? (8 + 2 + 2 * cipher_count) : 0;
199 const uint16_t ctx_count = offer_enc ? 3 : 2;
200 const size_t total = ctx_start + preauth_ctx + preauth_pad + sign_ctx + sign_pad + enc_ctx;
207 if (pc_smb2_build_header(buf, cap, Smb2Command::SMB2_NEGOTIATE, 1, 0, 0, 0) == 0)
213 uint8_t *b = buf + PC_SMB2_HEADER_SIZE;
214 memset(b, 0, ctx_start - PC_SMB2_HEADER_SIZE);
218 pc_wr32le(b + 8, Smb2GlobalCap::SMB2_GLOBAL_CAP_ENCRYPTION);
219 memcpy(b + 12, client_guid, 16);
222 for (uint16_t i = 0; i < ndialects; i++)
224 pc_wr16le(b + 36 + i * 2, (uint16_t)dialects[i]);
228 uint8_t *c = buf + ctx_start;
229 pc_wr16le(c + 0, Smb2NegotiateContextType::SMB2_PREAUTH_INTEGRITY_CAPABILITIES);
230 pc_wr16le(c + 2, (uint16_t)preauth_data);
234 pc_wr16le(c + 12, Smb2HashAlgorithm::SMB2_PREAUTH_INTEGRITY_SHA512);
235 memcpy(c + 14, salt, salt_len);
240 uint8_t *c2 = c + preauth_ctx + preauth_pad;
243 memset(c + preauth_ctx, 0, preauth_pad);
245 pc_wr16le(c2 + 0, Smb2NegotiateContextType::SMB2_SIGNING_CAPABILITIES);
249 pc_wr16le(c2 + 10, Smb2SigningAlgorithm::SMB2_SIGNING_AES_CMAC);
256 uint8_t *c3 = c2 + sign_ctx + sign_pad;
259 memset(c2 + sign_ctx, 0, sign_pad);
261 pc_wr16le(c3 + 0, Smb2NegotiateContextType::SMB2_ENCRYPTION_CAPABILITIES);
262 pc_wr16le(c3 + 2, (uint16_t)(2 + 2 * cipher_count));
264 pc_wr16le(c3 + 8, (uint16_t)cipher_count);
265 for (
size_t i = 0; i < cipher_count; i++)
273bool pc_smb2_parse_negotiate_contexts(
const uint8_t *msg,
size_t len, Smb2NegotiateContexts *out)
279 memset(out, 0,
sizeof(*out));
281 if (!pc_smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_NEGOTIATE)
285 if (len < PC_SMB2_HEADER_SIZE + 64)
289 const uint8_t *b = msg + PC_SMB2_HEADER_SIZE;
296 if (count == 0 || off < PC_SMB2_HEADER_SIZE)
302 for (uint16_t i = 0; i < count; i++)
304 p = (p + 7) & ~(
size_t)7;
312 if (data + dlen > len)
316 const uint8_t *d = msg + data;
317 if (ctype == Smb2NegotiateContextType::SMB2_PREAUTH_INTEGRITY_CAPABILITIES && dlen >= 6)
321 if (hcount >= 1 && (
size_t)dlen >= 4 + (
size_t)hcount * 2 + slen)
323 out->have_preauth =
true;
325 out->salt_len = slen;
326 out->salt = slen ? d + 4 + (size_t)hcount * 2 : nullptr;
329 else if (ctype == Smb2NegotiateContextType::SMB2_SIGNING_CAPABILITIES && dlen >= 4)
332 if (scount >= 1 && (
size_t)dlen >= 2 + (
size_t)scount * 2)
334 out->have_signing =
true;
335 out->signing_algorithm =
pc_rd16le(d + 2);
338 else if (ctype == Smb2NegotiateContextType::SMB2_ENCRYPTION_CAPABILITIES && dlen >= 4)
341 if (ccount >= 1 && (
size_t)dlen >= 2 + (
size_t)ccount * 2)
343 out->have_encryption =
true;
352void pc_smb_preauth_init(SmbPreauth *p)
356 memset(p->hash, 0,
sizeof(p->hash));
360void pc_smb_preauth_update(SmbPreauth *p,
const uint8_t *msg,
size_t len)
362 if (!p || (!msg && len))
368 uint8_t prev[PC_SMB2_PREAUTH_HASH_LEN];
369 memcpy(prev, p->hash,
sizeof(prev));
377size_t pc_smb2_build_session_setup(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id,
378 uint8_t security_mode,
const uint8_t *sec_buf,
size_t sec_len)
380 const size_t body = 24;
381 const size_t total = PC_SMB2_HEADER_SIZE + body + sec_len;
382 if (!buf || !sec_buf || sec_len == 0 || sec_len > 0xFFFF || cap < total)
387 if (pc_smb2_build_header(buf, cap, Smb2Command::SMB2_SESSION_SETUP, 1, message_id, 0, session_id) == 0)
393 uint8_t *b = buf + PC_SMB2_HEADER_SIZE;
397 b[3] = security_mode;
399 pc_wr16le(b + 12, (uint16_t)(PC_SMB2_HEADER_SIZE + body));
402 memcpy(b + body, sec_buf, sec_len);
406bool pc_smb2_parse_session_setup_response(
const uint8_t *msg,
size_t len, Smb2SessionSetupResp *out)
413 if (!pc_smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_SESSION_SETUP)
418 if (len < PC_SMB2_HEADER_SIZE + 8)
422 const uint8_t *b = msg + PC_SMB2_HEADER_SIZE;
433 out->sec_buf =
nullptr;
434 out->sec_buf_len = 0;
437 if ((
size_t)sec_off + sec_len > len || sec_off < PC_SMB2_HEADER_SIZE)
441 out->sec_buf = msg + sec_off;
442 out->sec_buf_len = sec_len;
446size_t pc_smb2_build_tree_connect(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id,
447 const uint8_t *path_utf16,
size_t path_len)
449 const size_t body = 8;
450 const size_t total = PC_SMB2_HEADER_SIZE + body + path_len;
451 if (!buf || !path_utf16 || path_len == 0 || path_len > 0xFFFF || cap < total)
456 if (pc_smb2_build_header(buf, cap, Smb2Command::SMB2_TREE_CONNECT, 1, message_id, 0, session_id) == 0)
462 uint8_t *b = buf + PC_SMB2_HEADER_SIZE;
466 pc_wr16le(b + 4, (uint16_t)(PC_SMB2_HEADER_SIZE + body));
468 memcpy(b + body, path_utf16, path_len);
472bool pc_smb2_parse_tree_connect_response(
const uint8_t *msg,
size_t len, Smb2TreeConnectResp *out)
479 if (!pc_smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_TREE_CONNECT)
483 if (len < PC_SMB2_HEADER_SIZE + 16)
487 const uint8_t *b = msg + PC_SMB2_HEADER_SIZE;
492 out->share_type = b[2];
499size_t pc_smb2_build_create(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id, uint32_t tree_id,
500 uint32_t desired_access, uint32_t share_access, uint32_t create_disposition,
501 uint32_t create_options,
const uint8_t *name_utf16,
size_t name_len)
503 const size_t body = 56;
504 const size_t total = PC_SMB2_HEADER_SIZE + body + name_len;
505 if (!buf || !name_utf16 || name_len == 0 || name_len > 0xFFFF || cap < total)
510 if (pc_smb2_build_header(buf, cap, Smb2Command::SMB2_CREATE, 1, message_id, tree_id, session_id) == 0)
516 uint8_t *b = buf + PC_SMB2_HEADER_SIZE;
527 pc_wr16le(b + 44, (uint16_t)(PC_SMB2_HEADER_SIZE + body));
530 memcpy(b + body, name_utf16, name_len);
534bool pc_smb2_parse_create_response(
const uint8_t *msg,
size_t len, Smb2CreateResp *out)
541 if (!pc_smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_CREATE)
545 if (len < PC_SMB2_HEADER_SIZE + 88)
549 const uint8_t *b = msg + PC_SMB2_HEADER_SIZE;
556 out->file_attributes =
pc_rd32le(b + 56);
557 memcpy(out->file_id, b + 64, 16);
561size_t pc_smb2_build_close(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id, uint32_t tree_id,
562 const uint8_t file_id[16])
564 const size_t body = 24;
565 const size_t total = PC_SMB2_HEADER_SIZE + body;
566 if (!buf || !file_id || cap < total)
571 if (pc_smb2_build_header(buf, cap, Smb2Command::SMB2_CLOSE, 1, message_id, tree_id, session_id) == 0)
577 uint8_t *b = buf + PC_SMB2_HEADER_SIZE;
581 memcpy(b + 8, file_id, 16);
585bool pc_smb2_parse_close_response(
const uint8_t *msg,
size_t len, Smb2CloseResp *out)
592 if (!pc_smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_CLOSE)
596 if (len < PC_SMB2_HEADER_SIZE + 60)
600 const uint8_t *b = msg + PC_SMB2_HEADER_SIZE;
606 out->file_attributes =
pc_rd32le(b + 56);
610size_t pc_smb2_build_read(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id, uint32_t tree_id,
611 const uint8_t file_id[16], uint32_t length, uint64_t offset)
613 const size_t body = 48;
614 const size_t total = PC_SMB2_HEADER_SIZE + body + 1;
615 if (!buf || !file_id || cap < total)
620 if (pc_smb2_build_header(buf, cap, Smb2Command::SMB2_READ, 1, message_id, tree_id, session_id) == 0)
626 uint8_t *b = buf + PC_SMB2_HEADER_SIZE;
627 memset(b, 0, body + 1);
630 (uint8_t)(PC_SMB2_HEADER_SIZE + 16);
634 memcpy(b + 16, file_id, 16);
641bool pc_smb2_parse_read_response(
const uint8_t *msg,
size_t len, Smb2ReadResp *out)
648 if (!pc_smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_READ)
652 if (len < PC_SMB2_HEADER_SIZE + 16)
656 const uint8_t *b = msg + PC_SMB2_HEADER_SIZE;
662 uint8_t data_off = b[2];
670 if (data_off < PC_SMB2_HEADER_SIZE || (
size_t)data_off + data_len > len)
674 out->data = msg + data_off;
675 out->data_len = data_len;
679size_t pc_smb2_build_write(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id, uint32_t tree_id,
680 const uint8_t file_id[16],
const uint8_t *data,
size_t data_len, uint64_t offset)
682 const size_t body = 48;
683 const size_t total = PC_SMB2_HEADER_SIZE + body + data_len;
684 if (!buf || !file_id || !data || data_len == 0 || data_len > 0xFFFFFFFF || cap < total)
689 if (pc_smb2_build_header(buf, cap, Smb2Command::SMB2_WRITE, 1, message_id, tree_id, session_id) == 0)
695 uint8_t *b = buf + PC_SMB2_HEADER_SIZE;
698 pc_wr16le(b + 2, (uint16_t)(PC_SMB2_HEADER_SIZE + body));
701 memcpy(b + 16, file_id, 16);
703 memcpy(b + body, data, data_len);
707bool pc_smb2_parse_write_response(
const uint8_t *msg,
size_t len, Smb2WriteResp *out)
714 if (!pc_smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_WRITE)
718 if (len < PC_SMB2_HEADER_SIZE + 16)
722 const uint8_t *b = msg + PC_SMB2_HEADER_SIZE;
736#define PC_SMB2_FLAGS_OFF 16
737#define PC_SMB2_SIGNATURE_OFF 48
738#define PC_SMB2_SIGNATURE_LEN 16
741typedef void (*Smb2MacFn)(
const uint8_t key[16],
const uint8_t *msg,
size_t len, uint8_t out16[16]);
743static void mac_hmac_sha256(
const uint8_t key[16],
const uint8_t *msg,
size_t len, uint8_t out16[16])
747 memcpy(out16, mac, 16);
750static void mac_aes_cmac(
const uint8_t key[16],
const uint8_t *msg,
size_t len, uint8_t out16[16])
755static void smb2_sign_framed(
const uint8_t key[16], uint8_t *msg,
size_t msg_len, Smb2MacFn mac)
757 if (!key || !msg || msg_len < PC_SMB2_HEADER_SIZE)
761 pc_wr32le(msg + PC_SMB2_FLAGS_OFF,
pc_rd32le(msg + PC_SMB2_FLAGS_OFF) | Smb2HeaderFlags::SMB2_FLAGS_SIGNED);
762 memset(msg + PC_SMB2_SIGNATURE_OFF, 0, PC_SMB2_SIGNATURE_LEN);
763 uint8_t tag[PC_SMB2_SIGNATURE_LEN];
764 mac(key, msg, msg_len, tag);
765 memcpy(msg + PC_SMB2_SIGNATURE_OFF, tag, PC_SMB2_SIGNATURE_LEN);
768static bool smb2_verify_framed(
const uint8_t key[16], uint8_t *msg,
size_t msg_len, Smb2MacFn mac)
770 if (!key || !msg || msg_len < PC_SMB2_HEADER_SIZE)
774 uint8_t received[PC_SMB2_SIGNATURE_LEN];
775 memcpy(received, msg + PC_SMB2_SIGNATURE_OFF, PC_SMB2_SIGNATURE_LEN);
776 memset(msg + PC_SMB2_SIGNATURE_OFF, 0, PC_SMB2_SIGNATURE_LEN);
777 uint8_t tag[PC_SMB2_SIGNATURE_LEN];
778 mac(key, msg, msg_len, tag);
779 memcpy(msg + PC_SMB2_SIGNATURE_OFF, received, PC_SMB2_SIGNATURE_LEN);
781 for (
size_t i = 0; i < PC_SMB2_SIGNATURE_LEN; i++)
783 diff |= (uint8_t)(tag[i] ^ received[i]);
788void pc_smb2_sign(
const uint8_t key[16], uint8_t *msg,
size_t msg_len)
790 smb2_sign_framed(key, msg, msg_len, mac_hmac_sha256);
793bool pc_smb2_verify(
const uint8_t key[16], uint8_t *msg,
size_t msg_len)
795 return smb2_verify_framed(key, msg, msg_len, mac_hmac_sha256);
798void pc_smb2_sign_cmac(
const uint8_t key[16], uint8_t *msg,
size_t msg_len)
800 smb2_sign_framed(key, msg, msg_len, mac_aes_cmac);
803bool pc_smb2_verify_cmac(
const uint8_t key[16], uint8_t *msg,
size_t msg_len)
805 return smb2_verify_framed(key, msg, msg_len, mac_aes_cmac);
808bool pc_smb3_derive_signing_key(
const uint8_t session_key[16], uint16_t dialect,
const uint8_t *preauth,
811 if (!session_key || !out_key)
822 if (dialect == (uint16_t)Smb2Dialect::SMB2_DIALECT_0311)
828 static const char label[] =
"SMBSigningKey";
829 memcpy(fixed + n, label,
sizeof(label));
832 memcpy(fixed + n, preauth, 64);
837 static const char label[] =
"SMB2AESCMAC";
838 static const char context[] =
"SmbSign";
839 memcpy(fixed + n, label,
sizeof(label));
842 memcpy(fixed + n, context,
sizeof(context));
843 n +=
sizeof(context);
857static bool smb3_derive_cipher_key(
const uint8_t session_key[16], uint16_t dialect,
const uint8_t *preauth,
bool c2s,
858 size_t key_len, uint8_t *out_key)
862 if (dialect == (uint16_t)Smb2Dialect::SMB2_DIALECT_0311)
868 static const char c2s_label[] =
"SMBC2SCipherKey";
869 static const char s2c_label[] =
"SMBS2CCipherKey";
870 memcpy(fixed + n, c2s ? c2s_label : s2c_label, sizeof(c2s_label));
871 n +=
sizeof(c2s_label);
873 memcpy(fixed + n, preauth, 64);
878 static const char label[] =
"SMB2AESCCM";
879 static const char ctx_in[] =
"ServerIn ";
880 static const char ctx_out[] =
"ServerOut";
881 memcpy(fixed + n, label,
sizeof(label));
884 memcpy(fixed + n, c2s ? ctx_in : ctx_out, sizeof(ctx_in));
887 const uint32_t l_bits = (uint32_t)(key_len * 8);
888 fixed[n++] = (uint8_t)((l_bits >> 24) & 0xff);
889 fixed[n++] = (uint8_t)((l_bits >> 16) & 0xff);
890 fixed[n++] = (uint8_t)((l_bits >> 8) & 0xff);
891 fixed[n++] = (uint8_t)(l_bits & 0xff);
895bool pc_smb3_derive_encryption_keys(
const uint8_t session_key[16], uint16_t dialect,
const uint8_t *preauth,
896 size_t key_len, uint8_t *out_c2s, uint8_t *out_s2c)
898 if (!session_key || !out_c2s || !out_s2c || (key_len != 16 && key_len != 32))
902 return smb3_derive_cipher_key(session_key, dialect, preauth,
true, key_len, out_c2s) &&
903 smb3_derive_cipher_key(session_key, dialect, preauth,
false, key_len, out_s2c);
906size_t pc_smb2_encrypt(uint16_t cipher,
const uint8_t *key,
const uint8_t nonce[PC_SMB2_NONCE_FIELD_LEN],
907 uint64_t session_id,
const uint8_t *msg,
size_t msg_len, uint8_t *out,
size_t out_cap)
909 const size_t key_len = pc_smb2_cipher_key_len(cipher);
910 const size_t nonce_len = pc_smb2_cipher_nonce_len(cipher);
911 if (!key || !nonce || !msg || !out || key_len == 0 || nonce_len == 0 ||
912 out_cap < PC_SMB2_TRANSFORM_HDR_LEN + msg_len)
919 memset(out, 0, PC_SMB2_TRANSFORM_HDR_LEN);
920 pc_wr32le(out + 0, PC_SMB2_TRANSFORM_PROTOCOL_ID);
921 memcpy(out + 20, nonce, PC_SMB2_NONCE_FIELD_LEN);
927 uint8_t *ct = out + PC_SMB2_TRANSFORM_HDR_LEN;
928 const uint8_t *aad = out + 20;
933 case Smb2Cipher::SMB2_ENCRYPTION_AES128_GCM: {
937 pc_aes128gcm_key *g = pc_aes128gcm_key_init(g_b.span().buf, key);
938 pc_aes128gcm_seal(g, out + 20, aad, 32, msg, msg_len, ct, tag);
939 pc_aes128gcm_key_wipe(g);
943 case Smb2Cipher::SMB2_ENCRYPTION_AES256_GCM: {
954 case Smb2Cipher::SMB2_ENCRYPTION_AES128_CCM:
955 case Smb2Cipher::SMB2_ENCRYPTION_AES256_CCM:
956 ok = pc_aesccm_seal_tag(key, key_len, out + 20, nonce_len, aad, 32, msg, msg_len, ct, tag);
965 memcpy(out + 4, tag, 16);
966 return PC_SMB2_TRANSFORM_HDR_LEN + msg_len;
969size_t pc_smb2_decrypt(uint16_t cipher,
const uint8_t *key,
const uint8_t *in,
size_t in_len, uint8_t *out,
972 const size_t key_len = pc_smb2_cipher_key_len(cipher);
973 const size_t nonce_len = pc_smb2_cipher_nonce_len(cipher);
974 if (!key || !in || !out || key_len == 0 || nonce_len == 0 || in_len < PC_SMB2_TRANSFORM_HDR_LEN)
978 if (
pc_rd32le(in + 0) != PC_SMB2_TRANSFORM_PROTOCOL_ID)
982 size_t ct_len = in_len - PC_SMB2_TRANSFORM_HDR_LEN;
983 if (
pc_rd32le(in + 36) != (uint32_t)ct_len || out_cap < ct_len)
995 memcpy(aad, in + 20, 32);
996 memcpy(tag, in + 4, 16);
997 const uint8_t *ct = in + PC_SMB2_TRANSFORM_HDR_LEN;
1001 case Smb2Cipher::SMB2_ENCRYPTION_AES128_GCM: {
1005 pc_aes128gcm_key *g = pc_aes128gcm_key_init(g_b.span().buf, key);
1006 ok = pc_aes128gcm_open(g, aad, aad, 32, ct, ct_len, tag, out);
1007 pc_aes128gcm_key_wipe(g);
1010 case Smb2Cipher::SMB2_ENCRYPTION_AES256_GCM: {
1020 case Smb2Cipher::SMB2_ENCRYPTION_AES128_CCM:
1021 case Smb2Cipher::SMB2_ENCRYPTION_AES256_CCM:
1022 ok = pc_aesccm_open_tag(key, key_len, aad, nonce_len, aad, 32, ct, ct_len, tag, out);
1027 return ok ? ct_len : 0;
AES-128 block cipher + AEAD_AES_128_GCM (RFC 5116 / NIST SP 800-38D).
void pc_aes_cmac(const uint8_t key[16], const uint8_t *msg, size_t msg_len, uint8_t mac[PC_AES_CMAC_LEN])
Compute the AES-128-CMAC of msg under key (RFC 4493).
AES-128-CMAC (RFC 4493 / NIST SP800-38B) one-shot MAC.
AEAD AES-CCM (NIST SP 800-38C / RFC 3610), 128- and 256-bit keys, detached tag.
AES-256-GCM AEAD (RFC 5116) - stateless, detached-tag API.
A secure borrow whose acquire and release are one call each.
Fixed-width integer serializers into a raw uint8_t* buffer - one source of truth.
size_t pc_wr64le(uint8_t *p, uint64_t v)
Write v little-endian at p.
uint64_t pc_rd64le(const uint8_t *p)
Read a little-endian u64 at p.
uint32_t pc_rd32le(const uint8_t *p)
Read a little-endian u32 at p.
size_t pc_wr32le(uint8_t *p, uint32_t v)
Write v little-endian at p.
uint16_t pc_rd16le(const uint8_t *p)
Read a little-endian u16 at p.
size_t pc_wr16le(uint8_t *p, uint16_t v)
Write v little-endian at p.
void pc_hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *data, size_t len, uint8_t mac[PC_HMAC_SHA256_LEN])
Compute HMAC-SHA2-256 over a single contiguous buffer.
HMAC-SHA2-256 (RFC 2104 + FIPS 198-1) - streaming context and one-shot API.
PC_CRYPTO_HOT bool pc_kdf_ctr_hmac_sha256(const uint8_t *ki, size_t ki_len, const uint8_t *fixed, size_t fixed_len, uint8_t *out, size_t out_len)
SP800-108 KDF in counter mode with HMAC-SHA256 as the PRF (NIST SP800-108 §5.1; r = 32-bit counter pl...
SP800-108 counter-mode key derivation (HMAC-SHA256 PRF).
bool pc_aesgcm_open(pc_aesgcm_key *k, const uint8_t nonce[PC_AESGCM_IV_LEN], const uint8_t *aad, size_t aad_len, const uint8_t *ct, size_t ct_len, const uint8_t tag[PC_AESGCM_TAG_LEN], uint8_t *out)
Open one record: verify tag over aad || ct in constant time, then (only on success) decrypt ct into o...
pc_aesgcm_key * pc_aesgcm_key_init(void *storage, const uint8_t key[PC_AESGCM_KEY_LEN])
Bind storage as a context keyed with key.
pc_cspan pc_aesgcm_seal(pc_aesgcm_key *k, const uint8_t nonce[PC_AESGCM_IV_LEN], const uint8_t *aad, size_t aad_len, const uint8_t *pt, size_t pt_len, uint8_t *ct_out, uint8_t tag_out[PC_AESGCM_TAG_LEN])
Seal one record under k and nonce.
void pc_aesgcm_key_wipe(pc_aesgcm_key *k)
Wipe the expanded schedule. Call on rekey and on close; the storage stays the caller's.
#define PC_WORK_AES128GCM
Secure pool accessor - borrows that hold key material.
void pc_sha512_update(pc_sha512_ctx *ctx, const uint8_t *data, size_t len)
Feed len bytes of data into the running hash.
PC_CRYPTO_HOT void pc_sha512_init(pc_sha512_ctx *ctx)
Initialize a streaming SHA-512 context (ctx must not be NULL).
void pc_sha512_final(pc_sha512_ctx *ctx, uint8_t digest[PC_SHA512_DIGEST_LEN])
Finalize the hash and write the 64-byte digest. The context is undefined afterwards; call init() agai...
SHA-512 (FIPS 180-4) - streaming context and one-shot API.
SMB2 client wire codec (MS-SMB2), PC_ENABLE_SMB - increment 1: the transport frame,...
Streaming SHA-512 context.