浏览代码

go.crypto/nacl: add package

R=golang-dev, dchest, r, rsc
CC=golang-dev
https://golang.org/cl/6497101
Adam Langley 13 年之前
父节点
当前提交
f09bfbbb56
共有 4 个文件被更改,包括 391 次插入0 次删除
  1. 85 0
      nacl/box/box.go
  2. 78 0
      nacl/box/box_test.go
  3. 155 0
      nacl/secretbox/secretbox.go
  4. 73 0
      nacl/secretbox/secretbox_test.go

+ 85 - 0
nacl/box/box.go

@@ -0,0 +1,85 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package box authenticates and encrypts messages using public-key cryptography.
+
+Box uses Curve25519, XSalsa20 and Poly1305 to encrypt and authenticate
+messages. The length of messages is not hidden.
+
+It is the caller's responsibility to ensure the uniqueness of nonces—for
+example, by using nonce 1 for the first message, nonce 2 for the second
+message, etc. Nonces are long enough that randomly generated nonces have
+negligible risk of collision.
+
+This package is interoperable with NaCl: http://nacl.cr.yp.to/box.html.
+*/
+package box
+
+import (
+	"code.google.com/p/go.crypto/curve25519"
+	"code.google.com/p/go.crypto/nacl/secretbox"
+	"code.google.com/p/go.crypto/salsa20/salsa"
+	"io"
+)
+
+// Overhead is the number of bytes of overhead when boxing a message.
+const Overhead = secretbox.Overhead
+
+// GenerateKey generates a new public/private key pair suitable for use with
+// Seal and Open.
+func GenerateKey(rand io.Reader) (publicKey, privateKey *[32]byte, err error) {
+	publicKey = new([32]byte)
+	privateKey = new([32]byte)
+	_, err = io.ReadFull(rand, privateKey[:])
+	if err != nil {
+		publicKey = nil
+		privateKey = nil
+		return
+	}
+
+	curve25519.ScalarBaseMult(publicKey, privateKey)
+	return
+}
+
+var zeros [16]byte
+
+// Precompute calculates the shared key between peersPublicKey and privateKey
+// and writes it to sharedKey. The shared key can be used with
+// OpenAfterPrecomputation and SealAfterPrecomputation to speed up processing
+// when using the same pair of keys repeatedly.
+func Precompute(sharedKey, peersPublicKey, privateKey *[32]byte) {
+	curve25519.ScalarMult(sharedKey, privateKey, peersPublicKey)
+	salsa.HSalsa20(sharedKey, &zeros, sharedKey, &salsa.Sigma)
+}
+
+// Seal appends an encrypted and authenticated copy of message to out, which
+// will be Overhead bytes longer than the original and must not overlap. The
+// nonce must be unique for each distinct message for a given pair of keys.
+func Seal(out, message []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) []byte {
+	var sharedKey [32]byte
+	Precompute(&sharedKey, peersPublicKey, privateKey)
+	return secretbox.Seal(out, message, nonce, &sharedKey)
+}
+
+// SealAfterPrecomputation performs the same actions as Seal, but takes a
+// shared key as generated by Precompute.
+func SealAfterPrecomputation(out, message []byte, nonce *[24]byte, sharedKey *[32]byte) []byte {
+	return secretbox.Seal(out, message, nonce, sharedKey)
+}
+
+// Open authenticates and decrypts a box produced by Seal and appends the
+// message to out, which must not overlap box. The output will be Overhead
+// bytes smaller than box.
+func Open(out, box []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) ([]byte, bool) {
+	var sharedKey [32]byte
+	Precompute(&sharedKey, peersPublicKey, privateKey)
+	return secretbox.Open(out, box, nonce, &sharedKey)
+}
+
+// OpenAfterPrecomputation performs the same actions as Open, but takes a
+// shared key as generated by Precompute.
+func OpenAfterPrecomputation(out, box []byte, nonce *[24]byte, sharedKey *[32]byte) ([]byte, bool) {
+	return secretbox.Open(out, box, nonce, sharedKey)
+}

+ 78 - 0
nacl/box/box_test.go

@@ -0,0 +1,78 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package box
+
+import (
+	"bytes"
+	"crypto/rand"
+	"encoding/hex"
+	"testing"
+
+	"code.google.com/p/go.crypto/curve25519"
+)
+
+func TestSealOpen(t *testing.T) {
+	publicKey1, privateKey1, _ := GenerateKey(rand.Reader)
+	publicKey2, privateKey2, _ := GenerateKey(rand.Reader)
+
+	if *privateKey1 == *privateKey2 {
+		t.Fatalf("private keys are equal!")
+	}
+	if *publicKey1 == *publicKey2 {
+		t.Fatalf("public keys are equal!")
+	}
+	message := []byte("test message")
+	var nonce [24]byte
+
+	box := Seal(nil, message, &nonce, publicKey1, privateKey2)
+	opened, ok := Open(nil, box, &nonce, publicKey2, privateKey1)
+	if !ok {
+		t.Fatalf("failed to open box")
+	}
+
+	if !bytes.Equal(opened, message) {
+		t.Fatalf("got %x, want %x", opened, message)
+	}
+
+	for i := range box {
+		box[i] ^= 0x40
+		_, ok := Open(nil, box, &nonce, publicKey2, privateKey1)
+		if ok {
+			t.Fatalf("opened box with byte %d corrupted", i)
+		}
+		box[i] ^= 0x40
+	}
+}
+
+func TestBox(t *testing.T) {
+	var privateKey1, privateKey2 [32]byte
+	for i := range privateKey1[:] {
+		privateKey1[i] = 1
+	}
+	for i := range privateKey2[:] {
+		privateKey2[i] = 2
+	}
+
+	var publicKey1 [32]byte
+	curve25519.ScalarBaseMult(&publicKey1, &privateKey1)
+	var message [64]byte
+	for i := range message[:] {
+		message[i] = 3
+	}
+
+	var nonce [24]byte
+	for i := range nonce[:] {
+		nonce[i] = 4
+	}
+
+	box := Seal(nil, message[:], &nonce, &publicKey1, &privateKey2)
+
+	// expected was generated using the C implementation of NaCl.
+	expected, _ := hex.DecodeString("78ea30b19d2341ebbdba54180f821eec265cf86312549bea8a37652a8bb94f07b78a73ed1708085e6ddd0e943bbdeb8755079a37eb31d86163ce241164a47629c0539f330b4914cd135b3855bc2a2dfc")
+
+	if !bytes.Equal(box, expected) {
+		t.Fatalf("box didn't match, got\n%x\n, expected\n%x", box, expected)
+	}
+}

+ 155 - 0
nacl/secretbox/secretbox.go

@@ -0,0 +1,155 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package secretbox encrypts and authenticates small messages.
+
+Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate messages with
+secret-key cryptography. The length of messages is not hidden.
+
+It is the caller's responsibility to ensure the uniqueness of nonces—for
+example, by using nonce 1 for the first message, nonce 2 for the second
+message, etc. Nonces are long enough that randomly generated nonces have
+negligible risk of collision.
+
+This package is interoperable with NaCl: http://nacl.cr.yp.to/secretbox.html.
+*/
+package secretbox
+
+import (
+	"code.google.com/p/go.crypto/poly1305"
+	"code.google.com/p/go.crypto/salsa20/salsa"
+)
+
+// Overhead is the number of bytes of overhead when boxing a message.
+const Overhead = poly1305.TagSize
+
+// setup produces a sub-key and Salsa20 counter given a nonce and key.
+func setup(subKey *[32]byte, counter *[16]byte, nonce *[24]byte, key *[32]byte) {
+	// We use XSalsa20 for encryption so first we need to generate a
+	// key and nonce with HSalsa20.
+	var hNonce [16]byte
+	copy(hNonce[:], nonce[:])
+	salsa.HSalsa20(subKey, &hNonce, key, &salsa.Sigma)
+
+	// The final 8 bytes of the original nonce form the new nonce.
+	copy(counter[:], nonce[16:])
+}
+
+// Seal appends an encrypted and authenticated copy of message to out, which
+// must not overlap message. The key and nonce pair must be unique for each
+// distinct message and the output will be Overhead bytes longer than message.
+func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
+	var subKey [32]byte
+	var counter [16]byte
+	setup(&subKey, &counter, nonce, key)
+
+	// The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
+	// Salsa20 works with 64-byte blocks, we also generate 32 bytes of
+	// keystream as a side effect.
+	var firstBlock [64]byte
+	salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
+
+	var poly1305Key [32]byte
+	copy(poly1305Key[:], firstBlock[:])
+
+	out = out[len(out):]
+	outLen := len(message) + poly1305.TagSize
+	if cap(out) >= outLen {
+		out = out[:outLen]
+	} else if out == nil {
+		out = make([]byte, outLen)
+	} else {
+		for i := 0; i < outLen; i++ {
+			out = append(out, 0)
+		}
+	}
+
+	// We XOR up to 32 bytes of message with the keystream generated from
+	// the first block.
+	firstMessageBlock := message
+	if len(firstMessageBlock) > 32 {
+		firstMessageBlock = firstMessageBlock[:32]
+	}
+
+	tagOut := out
+	out = out[poly1305.TagSize:]
+	for i, x := range firstMessageBlock {
+		out[i] = firstBlock[32+i] ^ x
+	}
+	message = message[len(firstMessageBlock):]
+	ciphertext := out
+	out = out[len(firstMessageBlock):]
+
+	// Now encrypt the rest.
+	counter[8] = 1
+	salsa.XORKeyStream(out, message, &counter, &subKey)
+
+	var tag [poly1305.TagSize]byte
+	poly1305.Sum(&tag, ciphertext, &poly1305Key)
+	copy(tagOut, tag[:])
+
+	return tagOut
+}
+
+// Open authenticates and decrypts a box produced by Seal and appends the
+// message to out, which must not overlap box. The output will be Overhead
+// bytes smaller than box.
+func Open(out []byte, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
+	if len(box) < Overhead {
+		return nil, false
+	}
+
+	var subKey [32]byte
+	var counter [16]byte
+	setup(&subKey, &counter, nonce, key)
+
+	// The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
+	// Salsa20 works with 64-byte blocks, we also generate 32 bytes of
+	// keystream as a side effect.
+	var firstBlock [64]byte
+	salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
+
+	var poly1305Key [32]byte
+	copy(poly1305Key[:], firstBlock[:])
+	var tag [poly1305.TagSize]byte
+	copy(tag[:], box)
+
+	if !poly1305.Verify(&tag, box[poly1305.TagSize:], &poly1305Key) {
+		return nil, false
+	}
+
+	out = out[len(out):]
+	outLen := len(box) - Overhead
+	if cap(out) >= outLen {
+		out = out[:outLen]
+	} else if out == nil {
+		out = make([]byte, outLen)
+	} else {
+		for i := 0; i < outLen; i++ {
+			out = append(out, 0)
+		}
+	}
+
+	// We XOR up to 32 bytes of box with the keystream generated from
+	// the first block.
+	box = box[Overhead:]
+	firstMessageBlock := box
+	if len(firstMessageBlock) > 32 {
+		firstMessageBlock = firstMessageBlock[:32]
+	}
+	for i, x := range firstMessageBlock {
+		out[i] = firstBlock[32+i] ^ x
+	}
+
+	box = box[len(firstMessageBlock):]
+	plaintext := out
+	out = out[len(firstMessageBlock):]
+
+	// Now decrypt the rest.
+	counter[8] = 1
+	salsa.XORKeyStream(out, box, &counter, &subKey)
+
+	return plaintext, true
+}

+ 73 - 0
nacl/secretbox/secretbox_test.go

@@ -0,0 +1,73 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package secretbox
+
+import (
+	"bytes"
+	"crypto/rand"
+	"encoding/hex"
+	"testing"
+)
+
+func TestSealOpen(t *testing.T) {
+	var key [32]byte
+	var nonce [24]byte
+
+	rand.Reader.Read(key[:])
+	rand.Reader.Read(nonce[:])
+
+	var box, opened []byte
+
+	for msgLen := 0; msgLen < 128; msgLen += 17 {
+		message := make([]byte, msgLen)
+		rand.Reader.Read(message)
+
+		box = Seal(box[:0], message, &nonce, &key)
+		var ok bool
+		opened, ok = Open(opened[:0], box, &nonce, &key)
+		if !ok {
+			t.Errorf("%d: failed to open box", msgLen)
+			continue
+		}
+
+		if !bytes.Equal(opened, message) {
+			t.Errorf("%d: got %x, expected %x", msgLen, opened, message)
+			continue
+		}
+	}
+
+	for i := range box {
+		box[i] ^= 0x20
+		_, ok := Open(opened[:0], box, &nonce, &key)
+		if ok {
+			t.Errorf("box was opened after corrupting byte %d", i)
+		}
+		box[i] ^= 0x20
+	}
+}
+
+func TestSecretBox(t *testing.T) {
+	var key [32]byte
+	var nonce [24]byte
+	var message [64]byte
+
+	for i := range key[:] {
+		key[i] = 1
+	}
+	for i := range nonce[:] {
+		nonce[i] = 2
+	}
+	for i := range message[:] {
+		message[i] = 3
+	}
+
+	box := Seal(nil, message[:], &nonce, &key)
+	// expected was generated using the C implementation of NaCl.
+	expected, _ := hex.DecodeString("8442bc313f4626f1359e3b50122b6ce6fe66ddfe7d39d14e637eb4fd5b45beadab55198df6ab5368439792a23c87db70acb6156dc5ef957ac04f6276cf6093b84be77ff0849cc33e34b7254d5a8f65ad")
+
+	if !bytes.Equal(box, expected) {
+		t.Fatalf("box didn't match, got\n%x\n, expected\n%x", box, expected)
+	}
+}