@@ -24,7 +24,7 @@ use self::{
24
24
} ;
25
25
use crate :: {
26
26
bits:: { BitLength , FromByteLen as _} ,
27
- cpu, debug,
27
+ cpu, debug, error ,
28
28
polyfill:: { self , slice, sliceutil} ,
29
29
} ;
30
30
use core:: num:: Wrapping ;
@@ -74,12 +74,19 @@ impl BlockContext {
74
74
// `num_pending < self.algorithm.block_len`.
75
75
//
76
76
// `block` may be arbitrarily overwritten.
77
- pub ( crate ) fn finish (
77
+ pub ( crate ) fn try_finish (
78
78
mut self ,
79
79
block : & mut [ u8 ; MAX_BLOCK_LEN ] ,
80
80
num_pending : usize ,
81
81
cpu_features : cpu:: Features ,
82
- ) -> Digest {
82
+ ) -> Result < Digest , FinishError > {
83
+ let completed_bytes = self
84
+ . completed_bytes
85
+ . checked_add ( polyfill:: u64_from_usize ( num_pending) )
86
+ . ok_or_else ( || FinishError :: too_much_input ( self . completed_bytes ) ) ?;
87
+ let copmleted_bits = BitLength :: from_byte_len ( completed_bytes)
88
+ . map_err ( |_: error:: Unspecified | FinishError :: too_much_input ( self . completed_bytes ) ) ?;
89
+
83
90
let block_len = self . algorithm . block_len ( ) ;
84
91
let block = & mut block[ ..block_len] ;
85
92
@@ -89,7 +96,11 @@ impl BlockContext {
89
96
padding
90
97
}
91
98
// Precondition violated.
92
- Some ( [ ] ) | None => unreachable ! ( ) ,
99
+ unreachable => {
100
+ return Err ( FinishError :: pending_not_a_partial_block (
101
+ unreachable. as_deref ( ) ,
102
+ ) ) ;
103
+ }
93
104
} ;
94
105
95
106
let padding = match padding
@@ -107,23 +118,17 @@ impl BlockContext {
107
118
}
108
119
} ;
109
120
110
- let completed_bytes = self
111
- . completed_bytes
112
- . checked_add ( polyfill:: u64_from_usize ( num_pending) )
113
- . unwrap ( ) ;
114
- let copmleted_bits = BitLength :: from_byte_len ( completed_bytes) . unwrap ( ) ;
115
-
116
121
let ( to_zero, len) = padding. split_at_mut ( padding. len ( ) - 8 ) ;
117
122
to_zero. fill ( 0 ) ;
118
123
len. copy_from_slice ( & copmleted_bits. to_be_bytes ( ) ) ;
119
124
120
125
let ( completed_bytes, leftover) = self . block_data_order ( block, cpu_features) ;
121
126
debug_assert_eq ! ( ( completed_bytes, leftover. len( ) ) , ( block_len, 0 ) ) ;
122
127
123
- Digest {
128
+ Ok ( Digest {
124
129
algorithm : self . algorithm ,
125
130
value : ( self . algorithm . format_output ) ( self . state ) ,
126
- }
131
+ } )
127
132
}
128
133
129
134
#[ must_use]
@@ -136,6 +141,37 @@ impl BlockContext {
136
141
}
137
142
}
138
143
144
+ pub ( crate ) enum FinishError {
145
+ #[ allow( dead_code) ]
146
+ TooMuchInput ( u64 ) ,
147
+ #[ allow( dead_code) ]
148
+ PendingNotAPartialBlock ( usize ) ,
149
+ }
150
+
151
+ impl FinishError {
152
+ #[ cold]
153
+ #[ inline( never) ]
154
+ fn too_much_input ( completed_bytes : u64 ) -> Self {
155
+ Self :: TooMuchInput ( completed_bytes)
156
+ }
157
+
158
+ // unreachable
159
+ #[ cold]
160
+ #[ inline( never) ]
161
+ fn pending_not_a_partial_block ( padding : Option < & [ u8 ] > ) -> Self {
162
+ match padding {
163
+ None => Self :: PendingNotAPartialBlock ( 0 ) ,
164
+ Some ( padding) => Self :: PendingNotAPartialBlock ( padding. len ( ) ) ,
165
+ }
166
+ }
167
+ }
168
+
169
+ impl From < FinishError > for error:: Unspecified {
170
+ fn from ( _: FinishError ) -> Self {
171
+ Self
172
+ }
173
+ }
174
+
139
175
/// A context for multi-step (Init-Update-Finish) digest calculations.
140
176
///
141
177
/// # Examples
@@ -227,10 +263,15 @@ impl Context {
227
263
///
228
264
/// `finish` consumes the context so it cannot be (mis-)used after `finish`
229
265
/// has been called.
230
- pub fn finish ( mut self ) -> Digest {
266
+ pub fn finish ( self ) -> Digest {
267
+ self . try_finish ( ) . unwrap ( )
268
+ }
269
+
270
+ fn try_finish ( mut self ) -> Result < Digest , error:: Unspecified > {
231
271
let cpu_features = cpu:: features ( ) ;
232
272
self . block
233
- . finish ( & mut self . pending , self . num_pending , cpu_features)
273
+ . try_finish ( & mut self . pending , self . num_pending , cpu_features)
274
+ . map_err ( error:: Unspecified :: from)
234
275
}
235
276
236
277
/// The algorithm that this context is using.
0 commit comments