85 lines
2.4 KiB
Go
85 lines
2.4 KiB
Go
package notifications
|
|
|
|
import (
|
|
"crypto/sha512"
|
|
"errors"
|
|
"crypto/sha256"
|
|
|
|
crand "crypto/rand"
|
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
|
"golang.org/x/crypto/pbkdf2"
|
|
)
|
|
|
|
// makeSessionKey returns pseudo-random symmetric key, which is used as
|
|
// session key between notification client and server
|
|
func makeSessionKey() ([]byte, error) {
|
|
// generate random key
|
|
const keyLen = 32
|
|
buf := make([]byte, keyLen)
|
|
_, err := crand.Read(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !validateSymmetricKey(buf) {
|
|
return nil, errors.New("error in GenerateSymKey: crypto/rand failed to generate random data")
|
|
}
|
|
|
|
key := buf[:keyLen]
|
|
derived, err := deriveKeyMaterial(key, whisper.EnvelopeVersion)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !validateSymmetricKey(derived) {
|
|
return nil, errors.New("failed to derive valid key")
|
|
}
|
|
|
|
return derived, nil
|
|
}
|
|
|
|
// validateSymmetricKey returns false if the key contains all zeros
|
|
func validateSymmetricKey(k []byte) bool {
|
|
return len(k) > 0 && !containsOnlyZeros(k)
|
|
}
|
|
|
|
// containsOnlyZeros checks if data is empty or not
|
|
func containsOnlyZeros(data []byte) bool {
|
|
for _, b := range data {
|
|
if b != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// deriveKeyMaterial derives symmetric key material from the key or password./~~~
|
|
// pbkdf2 is used for security, in case people use password instead of randomly generated keys.
|
|
func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) {
|
|
if version == 0 {
|
|
// kdf should run no less than 0.1 seconds on average compute,
|
|
// because it's a once in a session experience
|
|
derivedKey := pbkdf2.Key(key, nil, 65356, 32, sha256.New)
|
|
return derivedKey, nil
|
|
} else {
|
|
return nil, errors.New("unknown version")
|
|
}
|
|
}
|
|
|
|
// MakeTopic returns Whisper topic *as bytes array* by generating cryptographic key from the provided password
|
|
func MakeTopicAsBytes(password []byte) ([]byte) {
|
|
topic := make([]byte, int(whisper.TopicLength))
|
|
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
|
|
for i := 0; i < len(x); i++ {
|
|
topic[i%whisper.TopicLength] ^= x[i]
|
|
}
|
|
|
|
return topic
|
|
}
|
|
|
|
// MakeTopic returns Whisper topic by generating cryptographic key from the provided password
|
|
func MakeTopic(password []byte) (topic whisper.TopicType) {
|
|
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
|
|
for i := 0; i < len(x); i++ {
|
|
topic[i%whisper.TopicLength] ^= x[i]
|
|
}
|
|
|
|
return
|
|
}
|