Remix.run Logo
HendrikHensen 13 hours 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 12 hours ago | parent | next [-]

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.

voodooEntity 10 hours ago | parent [-]

Thanks, you brought up good points.

While in my case i had a program in which i created an instance of such a secret , "used it" and than scrambled the variable it never left so it worked.

Tho i didn't think of (2) which is especially problematic.

Prolly still would scramble on places its viable to implement, trying to reduce the surface even if i cannot fully remove it.

nemothekid 5 hours ago | parent | prev | next [-]

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 13 hours ago | parent | prev [-]

Yep thats what i had in mind

ok123456 11 hours ago | parent [-]

This proposal is worse because all the valuable regions of code will be clearly annotated for static analysis, either explicitly via a library/function call, or heuristically using the same boilerplate or fences.

voodooEntity 10 hours ago | parent [-]

Makes sense basically creating an easy to point out pattern for static analysis to find everything security related.

As another response pointed out, its also possible that said secret data is still in the register, which no matter what we do to the curr value could exist.

Thanks for pointing it out!

ok123456 10 hours ago | parent [-]

> Makes sense basically creating an easy to point out pattern for static analysis to find everything security related.

This is essentially already the case whenever you use encryption, because there are tell-tale signs you can detect (e.g., RSA S-Box). But this will make it even easier and also tip you off to critical sections that are sensitive yet don't involve encryption (e.g., secure strings).