top | item 46256497

(no title)

willahmad | 2 months ago

without language level support, it makes code look like a mess.

Imagine, 3 level nesting calls where each calls another 3 methods, we are talking about 28 functions each with couple of variables, of course you can still clean them up, but imagine how clean code will look if you don't have to.

Just like garbage collection, you can free up memory yourself, but someone forgot something and we have either memory leak or security issues.

discuss

order

HendrikHensen|2 months ago

With good helpers, it could become something as simple as

    key := make([]byte, 32)
    defer scramble(&key)
    // do all the secret stuff

Unless I don't understand the problem correctly.

kbolino|2 months ago

There are two main reasons why this approach isn't sufficient at a technical level, which are brought up by comments on the original proposal: https://github.com/golang/go/issues/21865

1) You are almost certainly going to be passing that key material to some other functions, and those functions may allocate and copy your data around; while core crypto operations could probably be identified and given special protection in their own right, this still creates a hole for "helper" functions that sit in the middle

2) The compiler can always keep some data in registers, and most Go code can be interrupted at any time, with the registers of the running goroutine copied to somewhere in memory temporarily; this is beyond your control and cannot be patched up after the fact by you even once control returns to your goroutine

So, even with your approach, (2) is a pretty serious and fundamental issue, and (1) is a pretty serious but mostly ergonomic issue. The two APIs also illustrate a basic difference in posture: secret.Do wipes everything except what you intentionally preserve beyond its scope, while scramble wipes only what you think it is important to wipe.

nemothekid|2 months ago

As I understand it, this is too brittle. I think this is trivially defeated if someone adds an append to your code:

    func do_another_important_thing(key []byte) []byte {
       newKey := append(key, 0x0, 0x1) // this might make a copy!
       return newKey
    } 

    key := make([]byte, 32) 
    defer scramble(&key) 
    do_another_important_thing(key)
    // do all the secret stuff

Because of the copy that append might do, you now have 2 copies of the key in data, but you only scramble one. There are many functions that might make a copy of the data given that you don't manually manage memory in Go. And if you are writing an open source library that might have dozens of authors, it's better for the language to provide a guarantee, rather than hope that a developer that probably isn't born yet will remember not to call an "insecure" function.

voodooEntity|2 months ago

Yep thats what i had in mind

compsciphd|2 months ago

I could imagine code that did something like this for primatives

  secretStash := NewSecretStash()
  pString := secretStash.NewString()
  ....
  ....
  secretStash.Thrash()
yes, you now have to deal in pointers, but that's not too ugly, and everything is stored in secretStash so can iterate over all the types it supports and thrash them to make them unusable, even without the gc running.

mbreese|2 months ago

I used to see this is bash scripts all the time. It’s somewhat gone out of favor (along with using long bash scripts).

If you had to prompt a user for a password, you’d read it in, use it, then thrash the value.

    read -p “Password: “ PASSWD
    # do something with $PASSWD
    PASSWD=“XXXXXXXXXXXXXXXXXX”
It’s not pretty, but a similar concept. (I also don't know how helpful it actually is, but that's another question...)

voodooEntity|2 months ago

Thats even better than what i had in mind but agree also a good way to just scrumble stuff unusable ++