1use crate::error::{Error, Result};
21use saorsa_pqc::api::sig::{ml_dsa_65, MlDsaPublicKey, MlDsaSignature, MlDsaVariant};
22use std::fs;
23use std::path::Path;
24use tracing::debug;
25
26pub const SIGNING_CONTEXT: &[u8] = b"saorsa-node-release-v1";
28
29pub const SIGNATURE_SIZE: usize = 3309;
31
32pub const PUBLIC_KEY_SIZE: usize = 1952;
34
35const RELEASE_SIGNING_KEY: &[u8] = &[
41 0xad, 0x56, 0x91, 0xe0, 0xab, 0x4a, 0xf6, 0x3a, 0x36, 0x0a, 0xc1, 0xcb, 0x5b, 0xd1, 0x16, 0x2a,
42 0xd8, 0xf6, 0xe7, 0x7c, 0xec, 0xb9, 0xd3, 0x23, 0xc6, 0x11, 0x86, 0x32, 0xb7, 0xc6, 0x2b, 0x8e,
43 0x12, 0xc5, 0xbb, 0x45, 0xe1, 0xc0, 0xd8, 0x6c, 0x02, 0x68, 0x2d, 0x77, 0xb2, 0x73, 0xa4, 0x77,
44 0x88, 0xac, 0xa2, 0x19, 0x39, 0xe7, 0x56, 0xea, 0x80, 0x8e, 0xbd, 0xd9, 0xa0, 0x69, 0xdd, 0x2e,
45 0x23, 0x3b, 0x72, 0xbe, 0x83, 0x45, 0xab, 0x1a, 0x04, 0x5d, 0x3b, 0x00, 0x02, 0xb9, 0xc9, 0xd1,
46 0xbf, 0x1e, 0x88, 0x44, 0xd7, 0x4a, 0x16, 0xd6, 0xc9, 0x8d, 0xb8, 0xb2, 0xdf, 0x07, 0x20, 0x30,
47 0x05, 0x7d, 0xc2, 0xff, 0x97, 0x4d, 0xc4, 0x42, 0xe5, 0xb9, 0xeb, 0xbc, 0x19, 0x1e, 0x8c, 0x6f,
48 0x06, 0x9b, 0x69, 0x47, 0x90, 0x1d, 0x91, 0x0f, 0x16, 0x62, 0x16, 0x7f, 0xeb, 0xac, 0x2b, 0xc0,
49 0xe5, 0x29, 0xc8, 0x1b, 0xb3, 0x59, 0x39, 0x43, 0xd7, 0xd2, 0xfd, 0xbd, 0x56, 0x94, 0x9d, 0x97,
50 0xec, 0xd1, 0xc9, 0x65, 0xbd, 0x36, 0x19, 0x5b, 0x2d, 0x17, 0x48, 0x84, 0xa0, 0xcf, 0x22, 0xf8,
51 0x3d, 0x70, 0x4a, 0xe6, 0x03, 0xfe, 0x65, 0xf7, 0xad, 0xdd, 0xfe, 0x6b, 0x1c, 0x27, 0xf3, 0x8b,
52 0x07, 0xf6, 0x81, 0x39, 0x84, 0xf3, 0x0f, 0x72, 0x31, 0x80, 0xf8, 0xe5, 0xc9, 0xf6, 0x90, 0x4a,
53 0x76, 0x2c, 0x04, 0xf9, 0xb2, 0x7c, 0x08, 0xbb, 0xdd, 0x35, 0x3e, 0xbc, 0x81, 0x0e, 0x1e, 0x71,
54 0xa3, 0x08, 0xf2, 0x74, 0xa2, 0x2b, 0xf2, 0x20, 0x4e, 0x3c, 0x41, 0xef, 0xea, 0x30, 0x45, 0x70,
55 0x35, 0x52, 0xbc, 0x89, 0x1a, 0xcf, 0x3d, 0x94, 0xf0, 0x62, 0xe8, 0xe3, 0x22, 0x28, 0x2e, 0xab,
56 0xc9, 0x22, 0x2f, 0xcd, 0x3e, 0xd3, 0x73, 0x9c, 0x25, 0x1c, 0xba, 0x38, 0xbe, 0x6b, 0x7a, 0x86,
57 0x82, 0xb9, 0x0f, 0x54, 0x5d, 0xa2, 0x0b, 0x4f, 0xd1, 0xce, 0xff, 0xe6, 0xc1, 0xaa, 0x26, 0x75,
58 0x06, 0xea, 0x3b, 0xc8, 0xf1, 0x03, 0x51, 0xc3, 0xd0, 0x03, 0x09, 0xb2, 0x0e, 0x20, 0xac, 0x33,
59 0x3d, 0x79, 0xd2, 0xf2, 0x51, 0x7f, 0x45, 0x23, 0x1e, 0x30, 0x64, 0x8c, 0x8c, 0x70, 0x61, 0xc6,
60 0x7d, 0xa7, 0xc0, 0x22, 0x83, 0x04, 0x03, 0xb3, 0x0e, 0x1b, 0x7f, 0xc2, 0xf0, 0xfb, 0xfc, 0x83,
61 0x14, 0x04, 0x66, 0xbe, 0xaa, 0x77, 0x8e, 0x88, 0x41, 0xd5, 0xa1, 0x57, 0xe0, 0xc6, 0x6e, 0x96,
62 0xbf, 0xe3, 0x27, 0x87, 0xc8, 0x03, 0xd8, 0xd4, 0x90, 0xc2, 0x89, 0x1c, 0x14, 0xb2, 0x92, 0x6b,
63 0xdf, 0xf0, 0x49, 0xff, 0x8a, 0xbe, 0xbc, 0x76, 0xf4, 0x62, 0x37, 0xa6, 0xf6, 0x91, 0x41, 0x28,
64 0x02, 0x68, 0xa8, 0xec, 0x17, 0xe7, 0xc9, 0xad, 0x40, 0xae, 0x11, 0xd6, 0x0a, 0x84, 0x6b, 0xe7,
65 0x0b, 0xae, 0xc8, 0x26, 0x58, 0x88, 0xb1, 0xf9, 0x9c, 0x8d, 0xdb, 0x16, 0x67, 0x60, 0x61, 0x84,
66 0xcc, 0x9b, 0xbb, 0x57, 0xd3, 0x20, 0xdb, 0x4e, 0x75, 0x22, 0x80, 0xda, 0xc3, 0x2b, 0xf3, 0x34,
67 0xcb, 0x7e, 0x1f, 0x06, 0x91, 0x36, 0xa4, 0xa5, 0x12, 0xfb, 0xcc, 0x81, 0xb0, 0xb3, 0x83, 0x8e,
68 0xe8, 0xf4, 0x1c, 0xfb, 0x9c, 0xb4, 0xea, 0x49, 0x49, 0x6b, 0xa8, 0x95, 0x7f, 0xb0, 0xac, 0xdf,
69 0x27, 0x0d, 0x0d, 0xab, 0xc3, 0xa1, 0x0f, 0x10, 0x1b, 0x1a, 0x0e, 0xbd, 0x2b, 0xbc, 0x1c, 0x9e,
70 0x8a, 0x9d, 0x88, 0xd4, 0xe9, 0x23, 0x3a, 0xd7, 0xe2, 0xad, 0x30, 0x35, 0x08, 0xae, 0x89, 0x8a,
71 0xca, 0xc2, 0x9d, 0xa9, 0xd0, 0x29, 0x4f, 0x50, 0xae, 0xe1, 0x75, 0xca, 0xc0, 0xaa, 0xed, 0x28,
72 0xfa, 0xa2, 0xab, 0x7c, 0x9e, 0xa7, 0x40, 0xce, 0xd8, 0xf7, 0xaa, 0xf7, 0x55, 0xb1, 0xcc, 0xd3,
73 0x83, 0xb5, 0x84, 0xd9, 0xcd, 0xef, 0xa5, 0x31, 0x78, 0x7a, 0xbc, 0x98, 0x02, 0xfb, 0xa7, 0x03,
74 0x0f, 0xac, 0x25, 0x81, 0x8f, 0xee, 0x4c, 0x85, 0xc6, 0x07, 0x68, 0xe0, 0x0d, 0x61, 0xdd, 0x59,
75 0xce, 0xbf, 0xce, 0xdb, 0x1d, 0x5c, 0x4b, 0xd3, 0x07, 0xeb, 0x39, 0x93, 0xe4, 0xc3, 0x56, 0xdb,
76 0x7a, 0xc7, 0x93, 0x18, 0xd6, 0x2b, 0xbf, 0xd4, 0xab, 0xe0, 0x42, 0x53, 0xc5, 0xf7, 0x26, 0xcd,
77 0xb6, 0x18, 0x14, 0x04, 0x5f, 0xa9, 0x74, 0xf6, 0x28, 0xfe, 0x45, 0x57, 0xa8, 0x96, 0x1f, 0x58,
78 0x10, 0x6e, 0x7c, 0x99, 0x69, 0xa9, 0xe9, 0x0e, 0x7e, 0x66, 0x89, 0xe5, 0x8f, 0xd9, 0xfa, 0xb2,
79 0x6b, 0x4c, 0xd7, 0xf0, 0x30, 0x22, 0x08, 0x46, 0x53, 0x5e, 0x7f, 0x88, 0x11, 0x7e, 0x4c, 0x97,
80 0xb8, 0x47, 0x0c, 0x58, 0xba, 0x62, 0xbc, 0x29, 0xf9, 0x06, 0x7b, 0xe0, 0xb9, 0x10, 0x87, 0x4e,
81 0x42, 0x89, 0x88, 0xe5, 0x5b, 0x5e, 0xe6, 0xdc, 0x86, 0xc9, 0x90, 0x6d, 0x06, 0xe5, 0x4d, 0x1e,
82 0x4b, 0x03, 0x90, 0xc7, 0x50, 0xf9, 0xa5, 0x85, 0x4b, 0x64, 0x6c, 0xcc, 0x1d, 0xc0, 0xf8, 0x67,
83 0xe4, 0xf1, 0x22, 0x4b, 0x52, 0x90, 0xe0, 0x64, 0x9d, 0xa1, 0xa0, 0x6b, 0xbf, 0xcc, 0x35, 0x3f,
84 0x1f, 0x5a, 0x0a, 0xd7, 0xb0, 0x25, 0x8f, 0xf8, 0xe1, 0xc2, 0xb9, 0xdb, 0x86, 0xc8, 0xc1, 0x6b,
85 0xca, 0x76, 0xc2, 0x6a, 0xad, 0xf6, 0x11, 0x58, 0xac, 0x89, 0xb2, 0x12, 0xd7, 0xa9, 0xaf, 0x99,
86 0x1d, 0xf4, 0xcb, 0x7e, 0xc7, 0x39, 0xaa, 0x2f, 0x35, 0x23, 0x0b, 0xc0, 0x63, 0xe2, 0xd0, 0x86,
87 0x8c, 0x2e, 0x2f, 0x86, 0x67, 0x21, 0x19, 0xf6, 0x51, 0x30, 0xa4, 0xd5, 0xc4, 0xfd, 0x85, 0xf7,
88 0x35, 0xaa, 0xd8, 0x5a, 0x1f, 0x16, 0x04, 0x76, 0xf7, 0xa9, 0x10, 0x46, 0x48, 0x56, 0xf7, 0xd2,
89 0xdc, 0x68, 0x18, 0x90, 0x2e, 0x82, 0x3b, 0x46, 0x27, 0xb3, 0xc6, 0xf2, 0xa0, 0xf6, 0x99, 0x70,
90 0x31, 0x37, 0x6a, 0x98, 0x3d, 0x09, 0xd0, 0xd4, 0x9f, 0xf2, 0x96, 0x6c, 0xad, 0x19, 0xf6, 0x65,
91 0x01, 0x8d, 0x06, 0x6d, 0xbb, 0xa4, 0x61, 0x29, 0x27, 0xe9, 0x8e, 0xad, 0x49, 0x2e, 0xee, 0x7c,
92 0xcd, 0xfe, 0xb2, 0x0e, 0xff, 0x66, 0x22, 0xf1, 0x21, 0xa8, 0xff, 0x21, 0x60, 0x13, 0x1b, 0x47,
93 0xc6, 0x5d, 0xe0, 0x76, 0x56, 0x71, 0x1e, 0x11, 0x58, 0x76, 0x22, 0x71, 0x91, 0x93, 0x74, 0x0f,
94 0x21, 0x35, 0x84, 0x24, 0x78, 0xa3, 0xa7, 0xe8, 0x82, 0x82, 0x6c, 0x2c, 0xa6, 0x80, 0x8d, 0xaf,
95 0x46, 0x42, 0x6d, 0xe6, 0x14, 0x96, 0x7a, 0xfc, 0xbc, 0x98, 0x89, 0x9b, 0x95, 0x71, 0x2e, 0x7a,
96 0xc3, 0x00, 0x21, 0x5b, 0x7e, 0x64, 0x62, 0x43, 0xcb, 0x4e, 0x79, 0x0a, 0xa8, 0x6f, 0x11, 0x78,
97 0xf9, 0xc0, 0x11, 0x6e, 0xc2, 0x4d, 0x95, 0x6b, 0x54, 0xab, 0x44, 0x1c, 0xb6, 0x60, 0x6d, 0x25,
98 0x00, 0xfa, 0x37, 0x16, 0xa6, 0x5c, 0x2f, 0x16, 0xf5, 0x36, 0x89, 0xc1, 0xe2, 0x8d, 0x38, 0xe3,
99 0xd3, 0x21, 0x57, 0x41, 0xd4, 0x12, 0x32, 0xfe, 0x3b, 0x90, 0x0f, 0x91, 0x2d, 0xe4, 0xaf, 0x57,
100 0xed, 0x31, 0x95, 0xf9, 0xab, 0x33, 0xac, 0xc4, 0xfb, 0xfc, 0xfa, 0xd1, 0x3e, 0xf5, 0x1e, 0x87,
101 0x0d, 0x30, 0x9a, 0x95, 0x57, 0xc6, 0x95, 0x09, 0x6a, 0xc1, 0xb9, 0xe1, 0x07, 0xee, 0x9d, 0x1b,
102 0x90, 0xf9, 0x90, 0xb6, 0xa8, 0x45, 0x66, 0xfc, 0x22, 0x86, 0xd7, 0x59, 0xeb, 0xd5, 0x6f, 0xe1,
103 0x61, 0x10, 0x7f, 0x51, 0x5b, 0xd0, 0x0c, 0xca, 0x2c, 0xd3, 0x45, 0xd8, 0xa8, 0x1a, 0x77, 0x6b,
104 0x8e, 0xe1, 0xb3, 0xf0, 0x02, 0x5c, 0xaa, 0x3e, 0x88, 0x12, 0xa9, 0xb0, 0xb0, 0x51, 0xbe, 0xc3,
105 0x74, 0xe0, 0x2c, 0xdc, 0x00, 0x97, 0x54, 0x7c, 0x3c, 0x4f, 0xd4, 0x46, 0x4d, 0xc6, 0xad, 0x70,
106 0xf5, 0x02, 0xae, 0xca, 0xc0, 0xd4, 0x0a, 0x81, 0x17, 0x84, 0x7d, 0x25, 0x76, 0x5d, 0xcb, 0xab,
107 0xbe, 0x37, 0x51, 0x52, 0xc1, 0x12, 0xbb, 0x1f, 0x41, 0x61, 0x87, 0x46, 0xab, 0x1e, 0xc8, 0x7b,
108 0xf9, 0xa7, 0x36, 0x35, 0x68, 0x34, 0xb6, 0xa3, 0x13, 0xc8, 0x05, 0xe9, 0x6f, 0x2f, 0xb5, 0xac,
109 0xf2, 0xde, 0xba, 0xee, 0xaa, 0xfa, 0xda, 0x9c, 0xe6, 0x2c, 0xa4, 0x6e, 0x8a, 0x9b, 0x29, 0x58,
110 0xd9, 0x68, 0x34, 0x12, 0x28, 0x6b, 0x82, 0xde, 0x13, 0x99, 0xa3, 0x63, 0x81, 0xf7, 0x82, 0xdb,
111 0xe4, 0x49, 0x24, 0xc7, 0x0c, 0xdb, 0xa5, 0xfb, 0xdb, 0x31, 0xbe, 0x3f, 0x66, 0xe7, 0x6c, 0x74,
112 0x38, 0x21, 0x96, 0x25, 0x47, 0xec, 0x84, 0x30, 0x75, 0x13, 0xbf, 0xf6, 0x57, 0xa1, 0xeb, 0xd4,
113 0x83, 0x36, 0x88, 0x8f, 0x83, 0x58, 0x56, 0xbe, 0x5a, 0x07, 0x6a, 0x1b, 0x59, 0x26, 0x45, 0x79,
114 0xff, 0x75, 0x66, 0x6e, 0xd8, 0x31, 0x1a, 0x8d, 0x37, 0x38, 0xfa, 0x40, 0x7f, 0x80, 0x14, 0x6e,
115 0x43, 0xc7, 0xa7, 0x8c, 0xf9, 0x9c, 0xd6, 0x98, 0x84, 0xe7, 0x81, 0xe8, 0xd0, 0xf5, 0xba, 0x8c,
116 0x2a, 0x8f, 0x29, 0xc8, 0x42, 0xba, 0xa0, 0xc2, 0x74, 0x1a, 0x76, 0x3c, 0xf5, 0x5d, 0xb1, 0x73,
117 0x84, 0x53, 0x3c, 0x6e, 0xa5, 0x31, 0xcd, 0x6d, 0x66, 0x2c, 0x02, 0x96, 0x0a, 0x95, 0x19, 0xeb,
118 0x8c, 0x6e, 0xb4, 0xd1, 0x23, 0xa5, 0x69, 0x5b, 0xcd, 0x27, 0x76, 0xcc, 0xb8, 0xb9, 0x60, 0xdf,
119 0x82, 0xca, 0xd8, 0x5b, 0xf8, 0x4e, 0x71, 0x07, 0xaf, 0xf8, 0x20, 0x0e, 0x33, 0xe8, 0x8a, 0x81,
120 0x21, 0x87, 0x46, 0x61, 0xb8, 0x39, 0xb8, 0x3f, 0x10, 0x83, 0x17, 0x19, 0x7b, 0xb3, 0x45, 0xfc,
121 0xc5, 0xb9, 0x40, 0xa7, 0xc7, 0x12, 0x3d, 0x87, 0xd9, 0x0c, 0xe8, 0xfe, 0xb3, 0x9a, 0x8e, 0x97,
122 0x14, 0xca, 0x66, 0x9e, 0xc1, 0x4d, 0x5a, 0x94, 0x27, 0x8c, 0x18, 0x5a, 0xcf, 0xd4, 0x08, 0xe3,
123 0x17, 0xbb, 0xb2, 0x72, 0xce, 0xf2, 0x79, 0x94, 0xa9, 0x74, 0x8e, 0x10, 0x20, 0xdc, 0x9f, 0xa9,
124 0x4f, 0x7c, 0x07, 0x72, 0xcf, 0xa3, 0x97, 0x49, 0xef, 0xf5, 0xbe, 0x56, 0x7c, 0x8a, 0xe1, 0x9e,
125 0xe7, 0x05, 0x85, 0xbe, 0xa5, 0xa8, 0x50, 0x2c, 0xbd, 0xd2, 0xc1, 0x5f, 0x2a, 0x57, 0x28, 0xd1,
126 0x64, 0x44, 0xde, 0xfe, 0x29, 0x69, 0xd9, 0x91, 0x79, 0x43, 0x67, 0x8c, 0x23, 0x96, 0xc5, 0x77,
127 0x55, 0x32, 0xc1, 0x74, 0xf5, 0x13, 0x81, 0xfd, 0xd6, 0x8a, 0x52, 0x14, 0xd0, 0x31, 0x16, 0x78,
128 0xce, 0x86, 0xdb, 0x1e, 0x20, 0xbf, 0x0f, 0xcf, 0x1d, 0x5b, 0xcf, 0x70, 0x67, 0x99, 0x69, 0x36,
129 0xaf, 0xe7, 0x87, 0x62, 0xee, 0xf7, 0xe1, 0xee, 0x8e, 0xb9, 0xd7, 0x8c, 0xc2, 0x4d, 0x96, 0xa6,
130 0x1c, 0x0e, 0xcd, 0xf5, 0xf8, 0x95, 0xbe, 0xa3, 0xb4, 0x24, 0x70, 0x31, 0xdd, 0xc1, 0x69, 0x7b,
131 0x36, 0xe3, 0x77, 0x56, 0xc2, 0xfb, 0xaa, 0xb8, 0xbf, 0xbc, 0x5a, 0x84, 0x1e, 0x48, 0xb4, 0xbd,
132 0x10, 0xf5, 0x3c, 0x0e, 0x8f, 0x8f, 0x29, 0x0b, 0xd3, 0x7b, 0x9b, 0x5f, 0x48, 0xd2, 0x70, 0xbd,
133 0xd8, 0xa4, 0xe6, 0x01, 0x6e, 0xf7, 0x2f, 0x6c, 0xac, 0xa4, 0xe5, 0x6c, 0x45, 0xa4, 0x1e, 0x71,
134 0x58, 0x3c, 0xb5, 0x6f, 0x22, 0x41, 0x10, 0x45, 0x33, 0x4a, 0x0c, 0xf1, 0xb7, 0x5a, 0x73, 0xb0,
135 0x20, 0xd4, 0x95, 0x81, 0xb7, 0x1c, 0xa1, 0x70, 0x40, 0xd4, 0xeb, 0x22, 0x6d, 0x4f, 0xca, 0xfe,
136 0x75, 0xbb, 0xf1, 0xa5, 0xff, 0x38, 0x06, 0x26, 0x06, 0xdf, 0x11, 0x07, 0xc8, 0xe1, 0x71, 0x71,
137 0x53, 0xdc, 0xaf, 0x44, 0xad, 0x6c, 0x1b, 0xa1, 0x41, 0xe3, 0xc0, 0xcf, 0x9b, 0xbf, 0x91, 0x55,
138 0x0c, 0x16, 0x24, 0xe4, 0x64, 0x30, 0x98, 0x0b, 0x2e, 0xb6, 0x52, 0xfe, 0x35, 0xc3, 0x54, 0x91,
139 0x56, 0x65, 0xf4, 0x69, 0xc3, 0x3a, 0x40, 0xcf, 0x67, 0xfb, 0x04, 0xba, 0x48, 0x51, 0xe9, 0xf4,
140 0xb8, 0xf9, 0x33, 0x95, 0x19, 0xe5, 0x6a, 0x53, 0x39, 0xdf, 0x18, 0xa9, 0x25, 0x2a, 0x9c, 0xcd,
141 0xdb, 0x1a, 0x2b, 0x83, 0xf4, 0xea, 0x6f, 0xb3, 0x84, 0x4d, 0x50, 0x5f, 0xd0, 0xf1, 0xbc, 0x8b,
142 0xcc, 0x8d, 0x8e, 0x16, 0x01, 0x3b, 0xfc, 0x4d, 0x62, 0xbb, 0xc7, 0x78, 0xfc, 0x22, 0x3e, 0x01,
143 0xf3, 0xfd, 0xb8, 0xb3, 0x39, 0x38, 0xed, 0x45, 0x03, 0x1b, 0xb0, 0x92, 0xe8, 0x84, 0xe8, 0x9a,
144 0xd7, 0x51, 0xdc, 0x5b, 0xd3, 0x3c, 0xfd, 0xe8, 0x01, 0x6c, 0xc5, 0x2c, 0x56, 0x70, 0x04, 0x42,
145 0x1a, 0xcf, 0x25, 0xc3, 0x13, 0x71, 0x8c, 0x12, 0x77, 0xea, 0xd8, 0x45, 0xcb, 0x4a, 0x2a, 0x82,
146 0xd7, 0x50, 0x84, 0x7d, 0x9f, 0xc9, 0x33, 0x3a, 0x0e, 0x72, 0x56, 0xa0, 0xec, 0xc1, 0x41, 0x66,
147 0xf6, 0x78, 0x52, 0x9d, 0x64, 0x22, 0xe8, 0xf4, 0x63, 0x4a, 0xf4, 0x64, 0x6a, 0xa5, 0xfe, 0x6f,
148 0x4d, 0x8f, 0xb2, 0x75, 0xf8, 0x68, 0xa8, 0xc4, 0x76, 0x73, 0x3c, 0x75, 0xf9, 0x88, 0xae, 0x39,
149 0x6f, 0x96, 0xbe, 0x01, 0x37, 0x3c, 0xef, 0xfd, 0xf2, 0x5f, 0x98, 0x95, 0x20, 0xbd, 0x41, 0x05,
150 0x75, 0x34, 0x80, 0x4f, 0x81, 0x02, 0xf1, 0x27, 0x4e, 0x1a, 0x24, 0xc7, 0xa8, 0xcd, 0xf0, 0xd6,
151 0xa1, 0x4e, 0xda, 0x59, 0x7e, 0xbd, 0xdf, 0x9c, 0xf5, 0xc5, 0xfa, 0x22, 0x2a, 0x2e, 0xcb, 0x4c,
152 0xa2, 0xbf, 0x2a, 0x66, 0x17, 0x0b, 0x82, 0x10, 0x86, 0x0a, 0x2f, 0xef, 0x82, 0xd9, 0x3c, 0xce,
153 0xe5, 0xde, 0xe9, 0xd9, 0x03, 0x0f, 0xce, 0x35, 0x24, 0xd7, 0x82, 0x75, 0x08, 0xb6, 0xad, 0x2c,
154 0x44, 0x3e, 0xcb, 0xe4, 0x28, 0xc7, 0x39, 0x58, 0xc0, 0xe4, 0xa8, 0x65, 0x5b, 0xfd, 0xf7, 0xa2,
155 0x91, 0xbc, 0x79, 0xb7, 0x99, 0x88, 0x0b, 0x1e, 0x70, 0x21, 0x22, 0x8e, 0x34, 0xc1, 0x6c, 0x40,
156 0x69, 0x01, 0x35, 0xc1, 0xf1, 0x95, 0x36, 0x19, 0xd4, 0xb4, 0x00, 0x7d, 0x68, 0xa1, 0x02, 0x6c,
157 0xa1, 0xe0, 0x61, 0x17, 0x6d, 0x0a, 0xdf, 0x2b, 0x03, 0x07, 0xc7, 0x21, 0x77, 0xcf, 0x45, 0x22,
158 0x7b, 0xb3, 0x26, 0xc2, 0xb8, 0x1a, 0x38, 0x79, 0xd7, 0xba, 0x8d, 0x9d, 0x33, 0x51, 0xcd, 0x74,
159 0xd2, 0x1a, 0x6f, 0x32, 0x52, 0xa4, 0xd3, 0xe9, 0xa9, 0xe2, 0x7f, 0xdf, 0x58, 0x2a, 0x73, 0x07,
160 0x2c, 0xee, 0x3e, 0x68, 0x8a, 0x27, 0xb2, 0xa3, 0x51, 0x4e, 0xf2, 0x73, 0xa4, 0xd1, 0x8f, 0xef,
161 0x62, 0x60, 0x92, 0x7c, 0x73, 0x2b, 0x58, 0x4e, 0xbe, 0xfd, 0x73, 0x1f, 0x0e, 0xed, 0x46, 0x50,
162 0x39, 0x4a, 0x66, 0x64, 0xd8, 0x11, 0x2d, 0x7b, 0x9d, 0xaf, 0xb8, 0x84, 0x20, 0x6d, 0x4e, 0xfb,
163];
164
165pub fn verify_binary_signature(binary_path: &Path, signature: &[u8]) -> Result<()> {
179 let public_key = MlDsaPublicKey::from_bytes(MlDsaVariant::MlDsa65, RELEASE_SIGNING_KEY)
180 .map_err(|e| Error::Crypto(format!("Invalid release key: {e}")))?;
181
182 verify_binary_signature_with_key(binary_path, signature, &public_key)
183}
184
185pub fn verify_binary_signature_with_key(
204 binary_path: &Path,
205 signature: &[u8],
206 public_key: &MlDsaPublicKey,
207) -> Result<()> {
208 debug!("Verifying signature for: {}", binary_path.display());
209
210 let binary_content = fs::read(binary_path).map_err(|e| {
212 Error::Crypto(format!(
213 "Failed to read binary '{}': {e}",
214 binary_path.display()
215 ))
216 })?;
217
218 if signature.len() != SIGNATURE_SIZE {
220 return Err(Error::Crypto(format!(
221 "Invalid signature size: expected {SIGNATURE_SIZE}, got {}",
222 signature.len()
223 )));
224 }
225
226 let sig = MlDsaSignature::from_bytes(MlDsaVariant::MlDsa65, signature)
228 .map_err(|e| Error::Crypto(format!("Invalid signature format: {e}")))?;
229
230 let dsa = ml_dsa_65();
232 let valid = dsa
233 .verify_with_context(public_key, &binary_content, &sig, SIGNING_CONTEXT)
234 .map_err(|e| Error::Crypto(format!("Signature verification error: {e}")))?;
235
236 if valid {
237 debug!("Signature verified successfully");
238 Ok(())
239 } else {
240 Err(Error::Crypto(
241 "Signature verification failed: invalid signature".to_string(),
242 ))
243 }
244}
245
246pub fn verify_from_file(binary_path: &Path, signature_path: &Path) -> Result<()> {
257 debug!(
258 "Verifying {} with signature from {}",
259 binary_path.display(),
260 signature_path.display()
261 );
262
263 let signature = fs::read(signature_path)?;
264 verify_binary_signature(binary_path, &signature)
265}
266
267pub fn verify_from_file_with_key(
279 binary_path: &Path,
280 signature_path: &Path,
281 public_key: &MlDsaPublicKey,
282) -> Result<()> {
283 debug!(
284 "Verifying {} with signature from {}",
285 binary_path.display(),
286 signature_path.display()
287 );
288
289 let signature = fs::read(signature_path)?;
290 verify_binary_signature_with_key(binary_path, &signature, public_key)
291}
292
293#[cfg(test)]
294#[allow(
295 clippy::unwrap_used,
296 clippy::expect_used,
297 clippy::cast_possible_truncation,
298 clippy::cast_sign_loss
299)]
300mod tests {
301 use super::*;
302 use saorsa_pqc::api::sig::ml_dsa_65;
303 use std::io::Write;
304 use tempfile::NamedTempFile;
305
306 #[test]
308 fn test_verify_valid_signature() {
309 let dsa = ml_dsa_65();
310 let (public_key, secret_key) = dsa.generate_keypair().unwrap();
311 let binary_content = b"test binary content for signing";
312
313 let mut file = NamedTempFile::new().unwrap();
314 file.write_all(binary_content).unwrap();
315
316 let signature = dsa
317 .sign_with_context(&secret_key, binary_content, SIGNING_CONTEXT)
318 .unwrap();
319
320 let result =
321 verify_binary_signature_with_key(file.path(), &signature.to_bytes(), &public_key);
322 assert!(result.is_ok(), "Valid signature should verify: {result:?}");
323 }
324
325 #[test]
327 fn test_reject_invalid_signature() {
328 let dsa = ml_dsa_65();
329 let (public_key, _) = dsa.generate_keypair().unwrap();
330 let binary_content = b"test binary content";
331
332 let mut file = NamedTempFile::new().unwrap();
333 file.write_all(binary_content).unwrap();
334
335 let invalid_sig = vec![0u8; SIGNATURE_SIZE];
337
338 let result = verify_binary_signature_with_key(file.path(), &invalid_sig, &public_key);
339 assert!(result.is_err(), "Invalid signature should be rejected");
340 }
341
342 #[test]
344 fn test_reject_wrong_key() {
345 let dsa = ml_dsa_65();
346 let (_, secret_key) = dsa.generate_keypair().unwrap();
347 let (wrong_key, _) = dsa.generate_keypair().unwrap();
348 let binary_content = b"test binary content";
349
350 let mut file = NamedTempFile::new().unwrap();
351 file.write_all(binary_content).unwrap();
352
353 let signature = dsa
354 .sign_with_context(&secret_key, binary_content, SIGNING_CONTEXT)
355 .unwrap();
356
357 let result =
358 verify_binary_signature_with_key(file.path(), &signature.to_bytes(), &wrong_key);
359 assert!(result.is_err(), "Wrong key should fail verification");
360 }
361
362 #[test]
364 fn test_reject_modified_binary() {
365 let dsa = ml_dsa_65();
366 let (public_key, secret_key) = dsa.generate_keypair().unwrap();
367 let original_content = b"original binary content";
368
369 let signature = dsa
371 .sign_with_context(&secret_key, original_content, SIGNING_CONTEXT)
372 .unwrap();
373
374 let mut file = NamedTempFile::new().unwrap();
376 file.write_all(b"MODIFIED binary content").unwrap();
377
378 let result =
379 verify_binary_signature_with_key(file.path(), &signature.to_bytes(), &public_key);
380 assert!(result.is_err(), "Modified binary should fail verification");
381 }
382
383 #[test]
385 fn test_reject_malformed_signature() {
386 let dsa = ml_dsa_65();
387 let (public_key, _) = dsa.generate_keypair().unwrap();
388 let binary_content = b"test content";
389
390 let mut file = NamedTempFile::new().unwrap();
391 file.write_all(binary_content).unwrap();
392
393 let short_sig = vec![0u8; 100];
395
396 let result = verify_binary_signature_with_key(file.path(), &short_sig, &public_key);
397 assert!(result.is_err(), "Malformed signature should be rejected");
398 assert!(
399 result
400 .unwrap_err()
401 .to_string()
402 .contains("Invalid signature size"),
403 "Error should mention invalid size"
404 );
405 }
406
407 #[test]
409 fn test_empty_file() {
410 let dsa = ml_dsa_65();
411 let (public_key, secret_key) = dsa.generate_keypair().unwrap();
412 let empty_content: &[u8] = b"";
413
414 let mut file = NamedTempFile::new().unwrap();
415 file.write_all(empty_content).unwrap();
416
417 let signature = dsa
418 .sign_with_context(&secret_key, empty_content, SIGNING_CONTEXT)
419 .unwrap();
420
421 let result =
422 verify_binary_signature_with_key(file.path(), &signature.to_bytes(), &public_key);
423 assert!(result.is_ok(), "Empty file should verify: {result:?}");
424 }
425
426 #[test]
428 fn test_nonexistent_file() {
429 let dsa = ml_dsa_65();
430 let (public_key, _) = dsa.generate_keypair().unwrap();
431 let path = Path::new("/nonexistent/path/to/binary");
432 let sig = vec![0u8; SIGNATURE_SIZE];
433
434 let result = verify_binary_signature_with_key(path, &sig, &public_key);
435 assert!(result.is_err(), "Non-existent file should fail");
436 }
437
438 #[test]
440 fn test_wrong_context_rejected() {
441 let dsa = ml_dsa_65();
442 let (public_key, secret_key) = dsa.generate_keypair().unwrap();
443 let content = b"binary content";
444
445 let mut file = NamedTempFile::new().unwrap();
446 file.write_all(content).unwrap();
447
448 let signature = dsa
450 .sign_with_context(&secret_key, content, b"wrong-context-string")
451 .unwrap();
452
453 let result =
455 verify_binary_signature_with_key(file.path(), &signature.to_bytes(), &public_key);
456 assert!(
457 result.is_err(),
458 "Wrong context should fail verification: {result:?}"
459 );
460 }
461
462 #[test]
464 fn test_verify_from_sig_file() {
465 let dsa = ml_dsa_65();
466 let (public_key, secret_key) = dsa.generate_keypair().unwrap();
467 let content = b"binary content for sig file test";
468
469 let mut binary_file = NamedTempFile::new().unwrap();
470 binary_file.write_all(content).unwrap();
471
472 let signature = dsa
473 .sign_with_context(&secret_key, content, SIGNING_CONTEXT)
474 .unwrap();
475
476 let mut sig_file = NamedTempFile::new().unwrap();
477 sig_file.write_all(&signature.to_bytes()).unwrap();
478
479 let result = verify_from_file_with_key(binary_file.path(), sig_file.path(), &public_key);
480 assert!(
481 result.is_ok(),
482 "Detached sig file verification should succeed: {result:?}"
483 );
484 }
485
486 #[test]
488 fn test_large_binary() {
489 let dsa = ml_dsa_65();
490 let (public_key, secret_key) = dsa.generate_keypair().unwrap();
491
492 let large_content: Vec<u8> = (0..1_000_000).map(|i| (i % 256) as u8).collect();
494
495 let mut file = NamedTempFile::new().unwrap();
496 file.write_all(&large_content).unwrap();
497
498 let signature = dsa
499 .sign_with_context(&secret_key, &large_content, SIGNING_CONTEXT)
500 .unwrap();
501
502 let result =
503 verify_binary_signature_with_key(file.path(), &signature.to_bytes(), &public_key);
504 assert!(result.is_ok(), "Large binary should verify: {result:?}");
505 }
506
507 #[test]
509 fn test_release_key_configured() {
510 assert_eq!(
512 RELEASE_SIGNING_KEY.len(),
513 PUBLIC_KEY_SIZE,
514 "Embedded release key should be {PUBLIC_KEY_SIZE} bytes"
515 );
516
517 let result = MlDsaPublicKey::from_bytes(MlDsaVariant::MlDsa65, RELEASE_SIGNING_KEY);
519 assert!(
520 result.is_ok(),
521 "Embedded release key should be valid ML-DSA-65 public key"
522 );
523 }
524
525 #[test]
527 fn test_embedded_key_rejects_invalid_signature() {
528 let content = b"test binary content";
529
530 let mut file = NamedTempFile::new().unwrap();
531 file.write_all(content).unwrap();
532
533 let invalid_sig = vec![0u8; SIGNATURE_SIZE];
535
536 let result = verify_binary_signature(file.path(), &invalid_sig);
537 assert!(result.is_err(), "Invalid signature should be rejected");
538 }
539}