mirror of
https://github.com/logos-messaging/noise.git
synced 2026-05-28 21:19:40 +00:00
Refactor to prepare for pipes implementation
This commit is contained in:
parent
39d629fcc7
commit
e36eb5dddd
108
box/box.go
108
box/box.go
@ -21,6 +21,7 @@ type Ciphersuite interface {
|
|||||||
DHLen() int
|
DHLen() int
|
||||||
CCLen() int
|
CCLen() int
|
||||||
MACLen() int
|
MACLen() int
|
||||||
|
KeyLen() (int, int)
|
||||||
GenerateKey(io.Reader) (Key, error)
|
GenerateKey(io.Reader) (Key, error)
|
||||||
|
|
||||||
DH(privkey, pubkey []byte) []byte
|
DH(privkey, pubkey []byte) []byte
|
||||||
@ -33,7 +34,7 @@ type CipherContext interface {
|
|||||||
Decrypt(authtext, ciphertext []byte) ([]byte, error)
|
Decrypt(authtext, ciphertext []byte) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const cvLen = 48
|
const CVLen = 48
|
||||||
|
|
||||||
type Key struct {
|
type Key struct {
|
||||||
Public []byte
|
Public []byte
|
||||||
@ -41,34 +42,34 @@ type Key struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Crypter struct {
|
type Crypter struct {
|
||||||
Cipher Ciphersuite
|
Cipher Ciphersuite
|
||||||
SenderKey Key
|
Key Key
|
||||||
ReceiverKey Key
|
PeerKey Key
|
||||||
ChainVar []byte
|
ChainVar []byte
|
||||||
KDFNum int
|
KDFNum int
|
||||||
|
|
||||||
scratch [192]byte
|
scratch [64]byte
|
||||||
cc CipherContext
|
cc CipherContext
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Crypter) noiseBody(cc CipherContext, dst []byte, padLen int, appData, header []byte) []byte {
|
func (c *Crypter) EncryptBody(dst, plaintext, authtext []byte, padLen int) []byte {
|
||||||
var plaintext []byte
|
var p []byte
|
||||||
if plainLen := len(appData) + padLen + 4; len(c.scratch) >= plainLen {
|
if plainLen := len(plaintext) + padLen + 4; len(c.scratch) >= plainLen {
|
||||||
plaintext = c.scratch[:plainLen]
|
p = c.scratch[:plainLen]
|
||||||
} else {
|
} else {
|
||||||
plaintext = make([]byte, plainLen)
|
p = make([]byte, plainLen)
|
||||||
}
|
}
|
||||||
copy(plaintext, appData)
|
copy(p, plaintext)
|
||||||
if _, err := io.ReadFull(rand.Reader, plaintext[len(appData):len(appData)+padLen]); err != nil {
|
if _, err := io.ReadFull(rand.Reader, p[len(plaintext):len(plaintext)+padLen]); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
binary.BigEndian.PutUint32(plaintext[len(appData)+padLen:], uint32(padLen))
|
binary.BigEndian.PutUint32(p[len(plaintext)+padLen:], uint32(padLen))
|
||||||
return cc.Encrypt(dst, header, plaintext)
|
return c.cc.Encrypt(dst, authtext, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Crypter) Encrypt(dst []byte, ephKey *Key, plaintext []byte, padLen int) ([]byte, error) {
|
func (c *Crypter) EncryptBox(dst []byte, ephKey *Key, plaintext []byte, padLen int) ([]byte, error) {
|
||||||
if len(c.ChainVar) == 0 {
|
if len(c.ChainVar) == 0 {
|
||||||
c.ChainVar = make([]byte, cvLen)
|
c.ChainVar = make([]byte, CVLen)
|
||||||
}
|
}
|
||||||
if ephKey == nil {
|
if ephKey == nil {
|
||||||
k, err := c.Cipher.GenerateKey(rand.Reader)
|
k, err := c.Cipher.GenerateKey(rand.Reader)
|
||||||
@ -85,22 +86,27 @@ func (c *Crypter) Encrypt(dst []byte, ephKey *Key, plaintext []byte, padLen int)
|
|||||||
dst = newDst
|
dst = newDst
|
||||||
}
|
}
|
||||||
|
|
||||||
dh1 := c.Cipher.DH(ephKey.Private, c.ReceiverKey.Public)
|
dh1 := c.Cipher.DH(ephKey.Private, c.PeerKey.Public)
|
||||||
dh2 := c.Cipher.DH(c.SenderKey.Private, c.ReceiverKey.Public)
|
dh2 := c.Cipher.DH(c.Key.Private, c.PeerKey.Public)
|
||||||
|
|
||||||
cv1, cc1 := c.deriveKey(dh1, c.ChainVar)
|
cv1, cc1 := c.deriveKey(dh1, c.ChainVar)
|
||||||
cv2, cc2 := c.deriveKey(dh2, cv1)
|
cv2, cc2 := c.deriveKey(dh2, cv1)
|
||||||
c.ChainVar = cv2
|
c.ChainVar = cv2
|
||||||
|
|
||||||
dst = append(dst, ephKey.Public...)
|
dst = append(dst, ephKey.Public...)
|
||||||
dst = c.cipher(cc1).Encrypt(dst, ephKey.Public, c.SenderKey.Public)
|
dst = c.cipher(cc1).Encrypt(dst, ephKey.Public, c.Key.Public)
|
||||||
return c.noiseBody(c.cipher(cc2), dst, padLen, plaintext, dst[dstPrefixLen:]), nil
|
c.cc.Reset(cc2)
|
||||||
|
return c.EncryptBody(dst, plaintext, dst[dstPrefixLen:], padLen), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Crypter) EncryptedLen(n int) int {
|
func (c *Crypter) EncryptedLen(n int) int {
|
||||||
return n + (2 * c.Cipher.DHLen()) + (2 * c.Cipher.MACLen()) + 4
|
return n + (2 * c.Cipher.DHLen()) + (2 * c.Cipher.MACLen()) + 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Crypter) SetContext(cc []byte) {
|
||||||
|
c.cipher(cc)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Crypter) cipher(cc []byte) CipherContext {
|
func (c *Crypter) cipher(cc []byte) CipherContext {
|
||||||
if c.cc == nil {
|
if c.cc == nil {
|
||||||
c.cc = c.Cipher.NewCipher(cc)
|
c.cc = c.Cipher.NewCipher(cc)
|
||||||
@ -110,13 +116,13 @@ func (c *Crypter) cipher(cc []byte) CipherContext {
|
|||||||
return c.cc
|
return c.cc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Crypter) Decrypt(ciphertext []byte) ([]byte, error) {
|
func (c *Crypter) DecryptBox(ciphertext []byte) ([]byte, error) {
|
||||||
if len(c.ChainVar) == 0 {
|
if len(c.ChainVar) == 0 {
|
||||||
c.ChainVar = make([]byte, cvLen)
|
c.ChainVar = make([]byte, CVLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
ephPubKey := ciphertext[:c.Cipher.DHLen()]
|
ephPubKey := ciphertext[:c.Cipher.DHLen()]
|
||||||
dh1 := c.Cipher.DH(c.ReceiverKey.Private, ephPubKey)
|
dh1 := c.Cipher.DH(c.Key.Private, ephPubKey)
|
||||||
cv1, cc1 := c.deriveKey(dh1, c.ChainVar)
|
cv1, cc1 := c.deriveKey(dh1, c.ChainVar)
|
||||||
|
|
||||||
header := ciphertext[:(2*c.Cipher.DHLen())+c.Cipher.MACLen()]
|
header := ciphertext[:(2*c.Cipher.DHLen())+c.Cipher.MACLen()]
|
||||||
@ -125,8 +131,13 @@ func (c *Crypter) Decrypt(ciphertext []byte) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if len(c.PeerKey.Public) > 0 {
|
||||||
|
if len(c.PeerKey.Public) != len(senderPubKey) || subtle.ConstantTimeCompare(senderPubKey, c.PeerKey.Public) != 1 {
|
||||||
|
return nil, errors.New("pipe: unexpected sender public key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dh2 := c.Cipher.DH(c.ReceiverKey.Private, senderPubKey)
|
dh2 := c.Cipher.DH(c.Key.Private, senderPubKey)
|
||||||
cv2, cc2 := c.deriveKey(dh2, cv1)
|
cv2, cc2 := c.deriveKey(dh2, cv1)
|
||||||
c.ChainVar = cv2
|
c.ChainVar = cv2
|
||||||
body, err := c.cipher(cc2).Decrypt(header, ciphertext)
|
body, err := c.cipher(cc2).Decrypt(header, ciphertext)
|
||||||
@ -138,33 +149,36 @@ func (c *Crypter) Decrypt(ciphertext []byte) ([]byte, error) {
|
|||||||
return body[:len(body)-(padLen+4)], nil
|
return body[:len(body)-(padLen+4)], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Crypter) deriveKey(dh, cv []byte) ([]byte, []byte) {
|
func (c *Crypter) DecryptBody(authtext, ciphertext []byte) ([]byte, error) {
|
||||||
// info || (byte)c || t[0:32] || extra_data
|
if c.cc == nil {
|
||||||
data := append(append(c.scratch[:0:128], cv...), 0)
|
return nil, errors.New("box: uninitialized cipher context")
|
||||||
data = data[:len(data)+32]
|
}
|
||||||
data = c.Cipher.AppendName(data)
|
return c.cc.Decrypt(authtext, ciphertext)
|
||||||
data = strconv.AppendInt(data, int64(c.KDFNum), 10)
|
|
||||||
|
|
||||||
t := c.scratch[cap(data):]
|
|
||||||
|
|
||||||
k := deriveKey(dh, data, t, cvLen, cvLen+c.Cipher.CCLen())
|
|
||||||
c.KDFNum++
|
|
||||||
return k[:cvLen], k[cvLen:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deriveKey(secret, data, t []byte, infoLen, outputLen int) []byte {
|
func (c *Crypter) deriveKey(dh, cv []byte) ([]byte, []byte) {
|
||||||
output := make([]byte, 0, outputLen)
|
extra := strconv.AppendInt(c.Cipher.AppendName(c.scratch[:0]), int64(c.KDFNum), 10)
|
||||||
|
k := DeriveKey(dh, extra, cv, CVLen+c.Cipher.CCLen())
|
||||||
|
c.KDFNum++
|
||||||
|
return k[:CVLen], k[CVLen:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeriveKey(secret, extra, info []byte, outputLen int) []byte {
|
||||||
|
buf := make([]byte, outputLen+sha512.Size)
|
||||||
|
output := buf[:0:outputLen]
|
||||||
|
t := buf[outputLen:]
|
||||||
h := hmac.New(sha512.New, secret)
|
h := hmac.New(sha512.New, secret)
|
||||||
var c byte
|
var c byte
|
||||||
for len(output) < outputLen {
|
for len(output) < outputLen {
|
||||||
data[infoLen] = c
|
h.Write(info)
|
||||||
copy(data[infoLen+1:], t[:32])
|
h.Write([]byte{c})
|
||||||
h.Write(data)
|
h.Write(t[:32])
|
||||||
|
h.Write(extra)
|
||||||
t = h.Sum(t[:0])
|
t = h.Sum(t[:0])
|
||||||
h.Reset()
|
h.Reset()
|
||||||
c++
|
c++
|
||||||
if cap(output)-len(output) < len(t) {
|
if outputLen-len(output) < len(t) {
|
||||||
output = append(output, t[:cap(output)-len(output)]...)
|
output = append(output, t[:outputLen-len(output)]...)
|
||||||
} else {
|
} else {
|
||||||
output = append(output, t...)
|
output = append(output, t...)
|
||||||
}
|
}
|
||||||
@ -195,6 +209,10 @@ func (noise255) GenerateKey(random io.Reader) (Key, error) {
|
|||||||
return Key{Private: privKey[:], Public: pubKey[:]}, nil
|
return Key{Private: privKey[:], Public: pubKey[:]}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (noise255) KeyLen() (int, int) {
|
||||||
|
return 32, 32
|
||||||
|
}
|
||||||
|
|
||||||
func (noise255) DH(privkey, pubkey []byte) []byte {
|
func (noise255) DH(privkey, pubkey []byte) []byte {
|
||||||
var dst, in, base [32]byte
|
var dst, in, base [32]byte
|
||||||
copy(in[:], privkey)
|
copy(in[:], privkey)
|
||||||
|
|||||||
@ -18,21 +18,21 @@ func (s *S) TestRoundtrip(c *C) {
|
|||||||
|
|
||||||
plain := []byte("yellow submarines")
|
plain := []byte("yellow submarines")
|
||||||
padLen := 2
|
padLen := 2
|
||||||
ciphertext, err := enc.Encrypt(nil, nil, plain, padLen)
|
ciphertext, err := enc.EncryptBox(nil, nil, plain, padLen)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
expectedLen := len(plain) + padLen + (2 * Noise255.DHLen()) + (2 * Noise255.MACLen()) + 4
|
expectedLen := len(plain) + padLen + (2 * Noise255.DHLen()) + (2 * Noise255.MACLen()) + 4
|
||||||
c.Assert(ciphertext, HasLen, expectedLen, Commentf("expected: %d", expectedLen))
|
c.Assert(ciphertext, HasLen, expectedLen, Commentf("expected: %d", expectedLen))
|
||||||
|
|
||||||
plaintext, err := dec.Decrypt(ciphertext)
|
plaintext, err := dec.DecryptBox(ciphertext)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(plaintext, DeepEquals, plain)
|
c.Assert(plaintext, DeepEquals, plain)
|
||||||
|
|
||||||
plain[0] = 'Y'
|
plain[0] = 'Y'
|
||||||
ciphertext, err = enc.Encrypt(nil, nil, plain, 0)
|
ciphertext, err = enc.EncryptBox(nil, nil, plain, 0)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
plaintext, err = dec.Decrypt(ciphertext)
|
plaintext, err = dec.DecryptBox(ciphertext)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Assert(plaintext, DeepEquals, plain)
|
c.Assert(plaintext, DeepEquals, plain)
|
||||||
}
|
}
|
||||||
@ -42,27 +42,27 @@ func newCrypters() (*Crypter, *Crypter) {
|
|||||||
sendKey, _ := Noise255.GenerateKey(rand.Reader)
|
sendKey, _ := Noise255.GenerateKey(rand.Reader)
|
||||||
|
|
||||||
enc := &Crypter{
|
enc := &Crypter{
|
||||||
Cipher: Noise255,
|
Cipher: Noise255,
|
||||||
SenderKey: sendKey,
|
Key: sendKey,
|
||||||
ReceiverKey: recvKey,
|
PeerKey: recvKey,
|
||||||
}
|
}
|
||||||
enc.ReceiverKey.Private = nil
|
enc.PeerKey.Private = nil
|
||||||
|
|
||||||
dec := &Crypter{
|
dec := &Crypter{
|
||||||
Cipher: Noise255,
|
Cipher: Noise255,
|
||||||
SenderKey: sendKey,
|
Key: recvKey,
|
||||||
ReceiverKey: recvKey,
|
PeerKey: sendKey,
|
||||||
}
|
}
|
||||||
dec.SenderKey.Private = nil
|
dec.PeerKey.Private = nil
|
||||||
|
|
||||||
return enc, dec
|
return enc, dec
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEncrypt(b *testing.B) {
|
func BenchmarkEncryptBox(b *testing.B) {
|
||||||
enc, _ := newCrypters()
|
enc, _ := newCrypters()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
enc.Encrypt(nil, nil, []byte("yellow submarine"), 0)
|
enc.EncryptBox(nil, nil, []byte("yellow submarine"), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user