Browser hacking: Implementing the Web Crypto hash functions in SerenityOS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome back so today we are gonna work on the browser um and specifically i would like to add this function here which i uh discovered a couple of days ago um and we don't implement yet so digest that is essentially um hashing functions for the web so all the sha or sha algorithms message a1 sure three sha256 four and five one two uh the nice thing is we already have these in the system so i'm not gonna do uh crypto stuff today so we're just gonna use the existing ones which is nice of course um because if they get optimized or whatever uh then we get that for free in the future and um i already should in theory know how to use them in serenity because uh a while ago i added the checksum utilities using the same functionality but today browser hacking um so i would say let's start with a little demo so that we can verify if it works um so essentially this takes a string converts it um or rather yeah using text encoder which i added recently just to make this demo work um encodes it to a area buffer uh oh no that's an uh unit 8 typed array which internally uses an area buffer and then we pass that to crypto.subtle.digest passing the string name essentially and then getting back an array buffer which we can turn back into a new 8 array and back into an array and then turning that into a hex string so let's start by adding that to welcome.js which is automatically run when we visit the browser start page um so i'm just gonna paste that can we get rid of that so the async function which is nice um will just work because various people added async support to lib.js recently so that's nice and then we can just put that here and pass the text how about well hello friends and then we have the string output and we can select that and let's also add some error handling um so this is promise based um hence why we use then and catch here um so error let's just that like that as well the name and message um because as you will notice um this initially facts because we don't have crypto that subtle yet we do have crypto on the window object but um this is what we're gonna add today um so let's give it a try running with a slightly larger system font today hopefully it's good enough and let's see so the browser will load the default page welcome.html and down here you can see our error undefined cannot be converted to an object so that's basically it's finding this it's not finding this which is then undefined it's trying to do a lookup and that fails so let's make that work i guess we're going to start by adding the an empty subtle crypto interface basically so that's defined over here and it's got a whole lot of functions we're just going to add digest today i'm looking for the spec which should be at the bottom yes here specification which is going to give us some idl which we can just copy and paste and let's put that next to crypto.idl which is over here and let's add a new file um that's going to be subtle crypto dot idl and then let's paste that and um that should be all and then in the next step we add the function but um first of all let's add the empty object so i guess we can basically um take crypto.h as a template because it's going to be very similar subtle crypto.h and also the cpp file nope typo reflector like that and this one added eden but that's gonna be me okay type okay right so we have the crypto namespace we have a software crypto object which is uh inheriting from linux or wrappable meaning we can expose it to javascript i don't think it needs to be weakable let's format that like this we're gonna have a cryptowrapper that's going to be subtle crypto wrapper which is automatically generated we need wrapable exception or and then i think we can just replace that right so this is the only function that's defined on the crypto interface currently um so it's got the i got random values here we're just gonna remove that for now it's gonna have a default constructor a subtle crypto wrapper and then we can teach it to wrap a subtle crypto object uh this is failing right now but should be fine later let's also add it to the cmac list over here and that's gonna be subtle crypto and there should be a yes up here so this is basically what generates the um wrappers um this is going to change very soon because andreas is literally right now working on trying to remove some of the wrapper stuff and making the objects um js objects directly but we're not gonna worry about that right now just keep going like that okay and then we got our empty interface here this should be generated this should all be fine don't need the value i think don't need that right now and this one can probably be empty right now yeah just remove that and then all the imports as well i guess i'm gonna need that in a little bit um and let's try if that build so far i think we'll also need to um let's look for crypto in libwap i think we also need to define it and add it to the global object somewhere subtle cryptos oh i'm in the wrong directory right right so this is where the uh crypto attribute is uh defined and we're gonna need something similar for crypto dot subtle and this is the crypto getter this is fine okay yes okay add window object interface which is basically adding the thing to the global object so we're gonna need an import i presume let's see bendings crypto crypto constructor yes okay uh this one now we're just gonna use the auto formatting so let's put that here subtle drop down and then down here like that this is failing oh it's not pausing the idl maybe it's not why is that not working does it want a semicolon at the end yes it does okay um maybe we need to declare this in the forwarding header yes okay it's quite nice when we got like a different um similar object already and then we can kind of orient based on that and makes it a little bit easier okay that is still failing let's have a look why um are we missing in includes probably reppable subtle crypto repo name spank bindings where's it declared oh it's up here okay we need to make a difference on forward declaration this one wrapper like that invalid static cast from ref counted crypto to time where's that failing now forward class crypto did we break something crypto subtle crypto and down here subtle crypto wrapper crypto wrapper subtle crypto wrapper crypto subtle crypto i'm not quite sure oh this is uh obviously a mistake let's try that again okay that built so what we should have now is the subtle crypto um constructor and that is uh not working because it's um not actually having a construct but it's still defined on the global object and let's confirm that works via the js console yes type error so what we should now have is much like crypto which is the crypto constructor we should have subtle crypto yes here we are okay and calling that should fail yes okay and also with new co that is working so next step is in crypto so here is a to do for read only attribute subtle crypto subtle so this is when you do um crypto dot sat2 this is uh we're on the crypto interface currently and then subtle is a attribute here so let's see if we can enable it and look for some other read only attribute if we can steal that we got css arm string [Music] dom string rule list um css style declaration uh say no change this one so we got a read-only attribute about signal signal so this should be on about controller so let's have a look there let's look at together first okay this is returning a another ref printer to abort signal so i guess let's just take that and put that on the crypto.h which is the implementation here and that's going to return a subtle crypto and it's called circle and now we need to make that so um i guess once you're constructing the crypto object we can make uh the subtle crypto at the same time so that would be down here let's change that move it out of the line and make a so how does it work in the abod controller it's got a non-ref pointer sure subtle okay and then it does yeah just creates the signal during a butt controller construction so we're gonna do the same for crypto like that and this is obviously gonna be a subtle crypto like that and should not take any arguments yes okay and that should be it and then let's see if that already works valid use of incomplete type okay i'm missing an include i presume so that's this [Music] okay now we're missing the rep function i believe i'm not sure that's defined for uh crypto i think it's not um oh down here okay and that is auto-generated okay so we might have to look in the wrapper generator doesn't know about crypto it does indeed um but that doesn't look special so where's that crypto prototype okay i think um it took me a little while to find uh we're missing a using declaration so it's not fully qualified so there's no cryptocurrency and then i found this total hack until we can figure out the namespace so let's add another total hack like that just adding decryptor namespace okay that did indeed work let's rebuild and have another look and let's see opening the browser and now this fails with a different error so now it's no longer failing with kind of commit undefined to an object but now it's actually having an object but failing to find the digest function which means we successfully added uh crypto dot subtle uh if i spell it properly that's pretty oh that's returning true okay that's not correct um that's a tricky issue so when you're missing an include um lib.js then goes and tries to convert the object pointer to a value and if it can't do that it uses the boolean constructor and that's how you get funny results like that okay so that's probably missing and include up here uh for this subtle crypto wrapper or that we simply include let's let's see um bought sigma wrapper down here let's try that all right let's give it a try open the browser again jsconsole crypto.subtle and yes we got indeed a subtle crypto instance uh so that's tricky when you're missing the include um it's making a boolean so i think we can make a first commit here uh just adding the empty subtle crypto interface copied from the spec directly um then adding the native underlying object for it it's fine thus we probably don't need to commit just yet so i'll select those adding the right stuff to header a wrapper generator um adding it to the window object yeah that's fine we don't want the welcome.js change obviously adding it to the forwarding header enabling that which was previously commented out um i think usually we use four spaces of indentation for idl but i'm not going to change that right now i want this and then adding the getter for it now that things thinks it's unused but it's still used by the generated files so that's fine what this one does and this one not just yet so that is the web at the add an empty subtle crypto interface add the subtle crypto interface just some boiler plate code to get started um this is both the uh s subtle crypto constructor to the when the object as well as the crypto dot subtle instance attribute use okay and then commit commit failed it might have reformatted something so let's try it again yes so we use pre-comment so sometimes when the commit initially fails it's worth checking if it just updated some files uh just reformatting stuff which is fine okay so now that we got the subtle attribute exposed i think we can start actually adding the function so let's look which one it is um up here digest i'm going to copy that into the idl file just like that and i believe we're gonna need buffer source and what is this type def object or dom string okay so you can either pass an object or a string i thought it was just just a string now like this that's a bit strange name okay oh so i think you can probably also add an object with a single name property i think we're going to skip this for now because we don't have type depths for idl and i don't think we have unions either so let's just skip the object part and make it the dump string with a proper fix me and the zoom port for either for this which is typedef okay promise we should support already and then buffer source let's have a look no no we're used yet okay that's fine let's have a look where it's defined redirecting and web idl okay it's also a typedef okay arraybuffer view or buffer so every buffer is fine and this is already typed arrays or a data view let's see i think we can hack something in the wrapper generator to make this work um let's look for promise because i remember adding that um yes this part so if it's a promise um so we're just gonna call that let's see i'm just gonna call that buffer source so we're hard coding that right here taking this branch copying it so if you say you have a type with the name buffer source like that so we're basically just hard-coding the behavior in the generator right now which is fine and then we want to check for if it's a typed array so first of all we need to check if it's not an object so if it's not an object we need to return an error i am pretty sure we got this somewhere yes like that so if it's not an object or if s object is not a typed array base should work because all the all the uh oh idea right so all the typed arrays inherit from typed array base and so we can simply check for that which we already do in a couple of places like here and that's going to cover all the different types arrays um so if it's not an object or if it's not a typed array or if it's not a arrow buffer i believe yes okay or if it's not a data view like this we might need some includes for that we don't use promise.h so maybe not and that's not an object of type parameter the type of name so essentially we're just um taking that probably yeah should be fine okay and then what do we pass to the function um so we could make a variant that's any of these but i think for now it's fine to just pass an object because we know it's like a js object so we just make a handle as object that should be fine so i mean in the function we're gonna know it's one of these types but let's keep it simple for now let's maybe leave it to do should we make this a variant okay and that's then gonna handle buffer source so let's see if we can add the implementation as well so this change was not necessary to make this bit work so it knows the type and what to generate for the code and then now we need an implementation of this basically so uh let's first have a look at the function itself if it returns an error or not um algorithm data be the result of getting a copy okay return a promise so it doesn't return the error directly it returns a promise or it returns a promise so this is fine we just need a implementation in subtle crypto so that's probably gonna return a promise pointer then um do we already have that promise any we do okay that's neat uh it's called promise in promise rejection event let's have a look at that oh i added it okay um promise yeah it's just gonna return a promise okay that should be fine so it's gonna need a namespace and js the js forwarding header should be enough for that and then it's called digest and it takes a where did it go right the string so that's going to be a dumb string in our case which is translated to a string counts reference and then uh the buffer source which is this is called algorithm and this is called a js object it's gonna be it's gonna be in handle a handle of an object does it work like that um probably yeah okay let's just try this and then we're gonna need that in supercrypto cpp now we obviously need to include the header file so include the web crypto subtle crypto and from lib.js we need premises it is in run time and then how about we just ignore that input for now make a new promise uh that auto it's gonna the namespace create i believe okay where do we get the global object from so we're in a ah this needs a subtle crypto specifier okay so where do we get that from um i think there's a wrapper yes okay so what do we need for that um probably is it called wrapper yes okay so get the wrapper which is our javascript object wrapping the native object and that i believe has a global object yes okay here we go passing that to here and let's just return that just to see that this um is that going to be a cons reference i'm not sure let's try it let's see if it builds okay this fails um i think that's just a missing header so a little look here um this is for the we have any literatures yes okay we have plenty all right implementations so we probably just want to add tagging no okay can a dynamic cast of type class js object to type data view target is not pointer or reference to complete type this is actually happening in the prototype okay so we might need degenerate prototype header implementation okay mary is simply needs to go here okay yeah that built so let's check out if the function is present and returns a unresolved empty promise basically opening the browser so down here we now get type error not an object of type buffer source okay um open js console we want crypto that's subtle dot digest that access okay and we need two arguments fine just a string and then make a new array buffer so how it works not an object of type buffer source okay um so where's that happening it's possible i messed that up oh yeah of course um we obviously want to check if it's not one of them and not not all of them so i'll just wrap that into parenthesis and then remove that and then remove that and let's add some spaces here right okay so if it's not an object or if it's not a typed array an array buffer or a data view then return the error otherwise continue let's try it again okay belts opening the browser no error down here because we probably returned a promise that never resolved which would explain it let's see crypto subtle digest we want a string and an array and that returns a promise okay nice um so we got this right so far let's continue with the actual implementation which is gonna be here so so far we ignored both of these arguments so let's look at the implementation so what i usually like to do is just copy all of this into the function body um as you might know i like having specification comments in the source code and let's just quickly fix that up like that okay at all the rhythm be the algorithm parameter pass to the digest method that's a no up because we got that here let data be the result of getting a copy of the bytes okay don't know that will work let normalize algorithm be the result of normalizing an algorithm um what does that mean defines a process for cursing inputs to a targeted idle dictionary type if arg is an instance of dom string run it with a new dictionary if it's object do all of this that looks really generic so does probably be being used in a bunch of other places and also dealing with the string and object at the same time so we might be able to simplify that so what does it actually do if registered algorithms contains a key that is case insensitive okay where is that defined supported algorithms digest let's look for something different two five six so i'm not really sure where that is defined but i mean it's probably just looking for a case insensitive match of um any of the hash function names so let's skip this and just to fix me this can this is way more generic than it needs to be right now so we skip it or rather we simply fired and then we just check right so this is where we actually want to get in hash algorithm so let me show you how that works by visiting the checksum utility from the userland which i implemented a long time ago and so basically we want this except if not the program name it's the algorithm name so and i think we have a um equals ignoring case yes that's perfect so we want this um so we have a hash kind so let's get that included first of all so this is coming from libcrypto crypto and then i believe it's hash and then we got a hash manager and i think yeah this is where hash kind is defined so we got all of these which is super neat um sha one hash kind is show one otherwise oh yeah okay what's the um like that yes okay none okay the default is now uh so we're clashing the namespace here because this is web.crypto so we need these columns so if we got sha1 then we got show one otherwise we check for um what other shares are two five six okay two five six we got 3 at 4 and then 512 like this um cool yeah so it's just checking that not used anywhere yet if an error occurred return a promise rejected okay so i guess now we need to look in that algorithm from earlier what kind of error they return normalize an algorithm otherwise return a new not supported error and terminate this algorithm so i guess we want an else so if it's none of these it's invalid and we just return that promise uh we reject it right and then we need a reason so let's wrap this in some braces and all of these as well like that okay and the error is going to be a not supported error which we should have somewhere in libweb yes so it's a dom exception basically got a bunch of those here and then i think we need to wrap that so let's look for rep error i'm trying to find a usage example right like this so by wrapping it we get a js value that we can then put in the promise that's gonna be not supported error which we need to get from somewhere uh oh it's missing dom prefix yes and then dumb exception probably and then it's missing what is it missing no matching call for rep um wait where is this um let's look for that again which rep is this um oh this is the dumb exception wrapper so we probably need to include that yes up here okay that makes sense and it works okay so i guess we just say invalid hash function and then we tell you you passed in so that's gonna be algorithm and that's our error and that we can simply pass here and just like that and then we'll return promise it's just gonna be like that if an error appeared return a parameter ejected with okay yeah some column right that promised be a new promise i guess that's handled earlier so that's already created up there just so that we don't need to make oh i guess we could let's just do it this way so promise and then we remove this in remove this down here make a new promise reject the return it does we still need to do which i'm gonna look into in a second return promise and perform okay in parallel so um this we're gonna cheat because um i don't think there's a good abstraction for that yet so i guess it's supposed to spawn a separate threat probably because if you hash huge amounts of data it would block the main threat and that's obviously going to affect the website negatively but for now we can get away with that and then we won't see once we have that abstraction we can do it properly which works nicely so we're just gonna skip that step and just do the hashing and then resolve the problems with result and return it so how do you resolve that you can reject but not resolve i'm very confused oh fulfill okay my bet and result is going to be performing the digest operation specified with data as message so data is our um our object okay so now we need to extract the array buffer oh no we get data up here okay getting a copy of the bytes so how does it work copy of the bytes that is defined in web idl and it's a 10 step function so let's look if we have that uh no okay let's just edit so where do we put that um i think there's some idl helpers somewhere idl oh yeah idea abstract operations let's just add myself next to look and then let's let's just call it that get buffer source copy and it returns bytes what is bytes new byte sequence of length so i believe this is probably best represented as a byte buffer which we can get from ack and then we're gonna pass a global object and buffer source okay so this is going to be an object which we know is either a data view an array buffer or a typed array that's fine okay and let's implement that so we are going to need the bypasser and then we are gonna need the global object and let's against just copy these steps it's gonna a bunch of white space which i fix up quickly okay here we go um it should be indented so should this so that yes buffer source be the result of converting buffer source to an ecmascript value that's already the case so it's a no up let es array buffer be ps yes buffer source okay um where's that being used down here okay let's just keep that alone for now let offset b0 what is offset by offset okay um [Music] okay it's probably just going to be a size t offset and sample length and then if the es buffer source um so i guess we can just call this espressos up here um because it's already an ecmascript value an object if it has a viewed array buffer internal slots and now we need to look up oh let's just look it up in our implementation so yeah this is typed array as you can see and let's look at data view viewed area buffer okay missing a comment but that's fine so if it is if it is a typed array base which we need to include if it is a typed array base or if it is a data view and then we need es array buffer okay so that's going to be an js array buffer pointer i believe let's include that as well and then so the es array buffer is now let's do it separately it's gonna be easy like that um it's gonna be we need to start a clustered to a typed array base object and then we need the viewed array buffer can we get that oh let's be const okay get array buffer okay cool and this is going to give us the pointer fine and then yes so i guess we will do this slightly different we named this back like that and then we can make it more convenient by adding a another variable which we call yes buffer source which is the properly casted object and then we need the offset which is byte offset which is u32 actually and so is this and then the same for the length and then let's repeat that down here basically can copy all of that so we need a casted object and this one's going to be the data view and then the rest is going to be the same [Music] maybe let's do it in the same branch i'm not sure now let's keep it like that okay data view is buffer source data view and this is going to be the same and otherwise we got a array buffer because there's no support for shared array buffers yet so we can that's where we verify cast that uh esports source is verify cast to an array buffer like that and then we just set the length um yes buffer source hmm arrival for length where did we get that from was it just length i think oh yeah it's just bad length okay cool and um do we have this i'm not sure apparently we do oh it's ah it's neat okay uh yes all right okay we set this we set this then i guess we also need to set it down here pro don't like that oh yeah okay that's just const casted turn js array buffer and then down here we got the pointer is non-null at this point so if that is sdtouch down here then return the empty byte sequence so if it is detached the exclamation mark here just means it can't throw so that's not a negation return presumably empty constructor yeah okay default constructor otherwise let bytes be a new byte sequence of length equal to length um okay so we need to make a new byte buffer create 0 it's going to return an optional so we're going to need error handling here because memory allocation might fail create zeroed length should be fine and then i guess if not oh can we just use try here i'm not sure if that's a thing for optional try optional does it have try functionality um it does not okay that's fine uh let's just do this then if not by it's the text value printer otherwise we fill in the data which is going to be a for loop and then we need probably no 64 to make the range fit because this is gonna use addition um in the range offset to offset plus length minus one so i is offset i lower than or equals to this increment um like that why don't you oh is it the weird minus okay sometimes in the spec they don't use the ascii plus and minus but like fancy characters okay set bytes at i minus offset to get value from buffer so i think this is defined on the array buffer um so the es array buffer should have a get value yes get value okay so we're passing in the es array buffer we just do it slightly differently because it's a method um get value 4 and u and eight so that's going to be u8 in our case and then it wants an index a boolean and an order so the index is i the boolean is true and the order is um unordered like that what don't you like that oh okay this is gonna need um how do you do this like that probably yeah okay okay and get value will return a value but since we already control the type we know for sure it's a numeric value that fits in on u8 so we can just say s u32 and then i guess we just cast that like that should be fine and that should be it so let's continue here let data be the result of getting a copy so we need to include the um idl abstract operations no where is it defined person bindings okay idl abstract operations and then you get what do we call it get buffer source copy oh it doesn't like it yet let's call it data array buffer and then it needs some namespace yes it's in the namespace okay and it wants the did we use the global object i don't think so uh this is missing okay yeah it's unused so we don't need a global object here which is fine just remove it and then down here we pass in right so we got a handle to an object so we can say data.cell i believe yes okay and then we need to dereference that because we already know it's not going to be nullpointer and then if that fails um i guess we could reject the promise as well i mean the spec doesn't handle this explicitly but i think it's probably fine to just do the same so if that returned an empty optional let's just do this we make a new promise we reject it and then instead of not supported error which one of these is fitting not found syntax namespace security abort error probably data clone data clone error maybe i really don't know which one is operation error let's just use operation error the operation failed for an operation specific reason yeah that's just useless don't overthink it failed to create a fail to copy bytes from array buffer which is essentially what's happening here don't need to format string and that should be it awesome okay and as the last step we need the result fix me we don't have a good abstraction for it yet so we do it synchronously think do it and sync um if the following okay so this is not going to throw us just um doing the hashing and then let's go back to the checksum utility and look up how the actual hashing works so we got hashkind which i guess we could do like this and then okay so hash what's that okay we make a hash manager and we initialize that um it's gonna be very straightforward um i believe ali initially added that and usually yeah yeah he makes good apis like that so it's going to be very convenient and then we just take this right so we make a hash manager called hash initialize it with the hash kind digest it and then we get data back okay and that we need to put into an array buffer so that's a consti 8 pointer um can we make an pipe buffer from that what constructor does have okay zero so this is we're going to check some oh digest size okay and just copies the data like that yeah okay so let's make let's make a byte buffer again if it fails we probably want to abort with a promise so um let's make that buffer results is a bite buffer create uh just zeroed off size this and then if that did fail we just use the same block as here um oh shadows okay we can reuse the promise i think okay failed to create result buffer and if it didn't fail we can just make a result here which is going to be an array buffer and i hope we can create one from [Music] the byte buffer which is called result buffer i need to include that and doesn't like it so what do we have we have bytebuffer pointer it's not going to help us though um oh so here it makes the buffer itself so i think that already works really it's like yeah okay we just need this essentially yes okay cool because we already support creating the array buffer we just don't have a factory method for it yet so this is just going to be this and then you pass the bypass over value and remove it which we can do here in the right function just move it doesn't like it why not um it should work i think why don't you like that oh because it's optional yes okay or we just release value i believe like that yeah cool cuckoo and then we pass that to the promise and now we just need to fill the result buffer so um [Music] let's just do it the slow way because i don't know the proper way we just loop the size and then put a value in the buffer which is the result buffer at index i and then we're gonna need digest immutable data at index i and we need to de-reference that and this should maybe work let's give it a try what don't you like um [Music] ideal abstract operations 98 this one okay um is it a missing include do we have array buffer we do hmm static oh static assertion failed it's basic maybe you can not use a verified costume i'm not really sure let's just start to cast it that's fine all right moment of truth did this work we will find out it crashed okay where did the crash um assertion failed we've got some out of bounds index somewhere so let's have a look at the traceback to make this very large um indigest fades at subtle crypto 73 73 okay this is um it doesn't like this oh is it like i think it's like that all right messed that up okay um i think there's a different loop then it's wrong yeah off by once that's a classic now let's try that again browser and it outputs something that's really cool okay uh let's i guess confirm if that's correct because i don't know the hash of whatever i put in there we tried well hello friends so this is sha256 so on linux that should work with printf um and then we pipe that to 256 some and it outputs a different string okay that is not good we messed that up somewhere okay i had to do a little bit of debugging of camera but i found the bug we forgot to initialize the data of the hash manager so we call hash.update we pass the data buffer from above so i guess it was always creating the hash of probably serial initialized memory which is silly but it's getting late and this finally works so that's really cool so uh let's quickly check the output so down here you see what we console.log which is the sha265 of well hello friends um or rather um yeah well hello friends and you can see on my linux machine the very same hash appears which means we got the implementation correct now so let's quickly turn this in a commit um and then we are finished so let's do this separately um which was just adding an array buffer over a load for a non-pointer but passing by value so that's going to be add great for which is a byte buffer and um let's commit that okay next up we got the whole implementation do we not need the wrapper here i think we do need to weapon for this um okay which is the whole implementation of digest with two fixed me's in it but that's totally fine um right let's do this also separately lib add support for the what's it called um [Music] buffer source idl per meter type that should be it and then let's do this separately which was um called get a copy of the bytes okay libre implement um i guess just that yeah okay and it failed probably because of formatting yes and then we add the subtle crypto interface uh the getter for digest or rather the function for digest um the function itself okay this one we don't want to add so it's just this implement um subtle crypto dot chest this is a sim the implementation of this function using lip crypto under the hood so it supports all of the required hashing hash functions and that's going to be show one two five six uh three at four i believe and five one two um two fixed means remain doing the hashing in parallel and supporting an object argument instead of a plain string do you not like using order all the fine cool okay uh that's gonna be it for today's video if you made it to the very end then i thank you for watching um this has taken much longer than expected but should be fine and see you soon bye
Info
Channel: Linus Groh
Views: 1,446
Rating: undefined out of 5
Keywords:
Id: JDawk7GiOnE
Channel Id: undefined
Length: 85min 3sec (5103 seconds)
Published: Tue Dec 14 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.