├── version.go ├── secretbox ├── README ├── testdata │ └── TEST.txt ├── secretbox.go └── secretbox_test.go ├── strongbox ├── README ├── testdata │ └── TEST.txt ├── strongbox.go └── strongbox_test.go ├── box ├── README ├── util.go ├── testdata │ └── TEST.txt ├── box.go └── box_test.go ├── stoutbox ├── README ├── util_test.go ├── util.go ├── testdata │ └── TEST.txt ├── stoutbox.go └── stoutbox_test.go ├── doc.go ├── LICENSE └── README /version.go: -------------------------------------------------------------------------------- 1 | package cryptobox 2 | 3 | const VersionString = "1.0.1" 4 | -------------------------------------------------------------------------------- /secretbox/README: -------------------------------------------------------------------------------- 1 | cryptobox/secretbox 2 | 3 | This is a NaCL-like implementation of a cryptographic system using 4 | FIPS-compliant ciphers. 5 | 6 | secret provides 20-year security using AES-128 in CTR mode with 7 | HMAC-SHA-256, assuming keys are not compromised. 8 | -------------------------------------------------------------------------------- /strongbox/README: -------------------------------------------------------------------------------- 1 | cryptobox/strongbox 2 | 3 | This is a NaCL-like implementation of a cryptographic system using 4 | FIPS-compliant ciphers. 5 | 6 | strongbox provides 50-year security using AES-256 in CTR mode with 7 | HMAC-SHA-384, assuming keys are not compromised. 8 | -------------------------------------------------------------------------------- /box/README: -------------------------------------------------------------------------------- 1 | cryptobox/box 2 | 3 | This is a NaCL-like implementation of a public-key cryptographic system 4 | using FIPS-compliant ciphers. 5 | 6 | box provides 20-year security using ECDH with the NIST P256 curve, 7 | and the secretbox package as the underlying symmetric encryption 8 | system. 9 | -------------------------------------------------------------------------------- /stoutbox/README: -------------------------------------------------------------------------------- 1 | cryptobox/stoutbox 2 | 3 | This is a NaCL-like implementation of a public-key cryptographic system 4 | using FIPS-compliant ciphers. 5 | 6 | stoutbox provides 50-year security using ECDH with the NIST P521 curve, 7 | and the strongbox package as the underlying symmetric encryption system. 8 | -------------------------------------------------------------------------------- /stoutbox/util_test.go: -------------------------------------------------------------------------------- 1 | package stoutbox 2 | 3 | import "testing" 4 | import "fmt" 5 | 6 | func TestU32(t *testing.T) { 7 | w := newbw(nil) 8 | w.WriteUint32(uint32(4)) 9 | 10 | b := w.Bytes() 11 | if b == nil { 12 | fmt.Println("Bwriter failed.") 13 | t.FailNow() 14 | } 15 | 16 | r := newbr(b) 17 | n, ok := r.NextU32() 18 | if !ok { 19 | fmt.Println("Breader failed.") 20 | t.FailNow() 21 | } else if n != 4 { 22 | fmt.Println("expected 4, got", n) 23 | t.FailNow() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoBox is a set of packages that provide core functions for 3 | safely employing cryptography, while using FIPS ciphers as the 4 | underlying cryptographic primitives. It is heavily inspired by 5 | [NaCL](http://nacl.cace-project.eu/) The aim is provide developers 6 | with a well-documented, properly-written, FIPS-compliant set of 7 | tools for proper encryption. CryptoBox is both the name of the 8 | suite and the name of module that is expexpected to be relevant 9 | to most developers. 10 | */ 11 | package cryptobox 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Kyle Isom 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | cryptobox 2 | 3 | Cryptobox aims to provide similar functionality to NaCL[1], while using 4 | FIPS ciphers. It provides the following packages: 5 | 6 | * box: secure and authenticate small messages with 20-year security 7 | using public key cryptography. 8 | * stoutbox: secure and authenticate small messages with 50-year security 9 | using public key cryptography. 10 | 11 | * secretbox: secure and authenticate small messages with 20-year security. 12 | * strongbox: secure and authenticate small messages with 50-year security. 13 | 14 | Developers should prefer the box and stoutbox packages, as these reduce the 15 | possibility of key compromise by using public keys. 16 | 17 | cryptobox operates on the concept of "boxes", which are encrypted and 18 | authenticated secure messages. Boxes operate under the following threat model: 19 | 20 | * Messages should be confidential, but it is not a concern that Eve can 21 | determine the length of the message. 22 | * The communicating parties have some means of authenticating keys on 23 | their own; that is, this package provides no authentication outside 24 | of the keys themselves. There is no identity bound to a key. 25 | 26 | 27 | License: 28 | 29 | cryptobox is released under the ISC license. See the include LICENSE 30 | file for details. 31 | 32 | 33 | See also: 34 | 35 | The Cryptobox project: http://cryptobox.tyrfingr.is/. This site contains 36 | the specification, including test vectors, and a guidebook for building 37 | secure systems using Cryptobox. 38 | -------------------------------------------------------------------------------- /box/util.go: -------------------------------------------------------------------------------- 1 | package box 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "math/big" 7 | ) 8 | 9 | const u32Len uint32 = 4 10 | 11 | func marshalSignature(r, s *big.Int) []byte { 12 | sig := newbw(nil) 13 | sig.Write(r.Bytes()) 14 | sig.Write(s.Bytes()) 15 | return sig.Bytes() 16 | } 17 | 18 | type bw struct { 19 | buf *bytes.Buffer 20 | err error 21 | } 22 | 23 | func newbw(init []byte) *bw { 24 | b := new(bw) 25 | b.buf = new(bytes.Buffer) 26 | if init != nil { 27 | b.buf.Write(init) 28 | } 29 | return b 30 | } 31 | 32 | func (b *bw) Write(data []byte) { 33 | if b.err != nil { 34 | return 35 | } 36 | b.err = binary.Write(b.buf, binary.BigEndian, uint32(len(data))) 37 | b.buf.Write(data) 38 | } 39 | 40 | func (b *bw) WriteUint32(n uint32) { 41 | if b.err != nil { 42 | return 43 | } 44 | b.err = binary.Write(b.buf, binary.BigEndian, u32Len) 45 | if b.err == nil { 46 | b.err = binary.Write(b.buf, binary.BigEndian, n) 47 | } 48 | } 49 | 50 | func (b *bw) Bytes() []byte { 51 | if b.err != nil { 52 | return nil 53 | } 54 | return b.buf.Bytes() 55 | } 56 | 57 | func unmarshalSignature(sig []byte) (r, s *big.Int) { 58 | b := newbr(sig) 59 | rb := b.Next() 60 | sb := b.Next() 61 | if rb != nil && sb != nil { 62 | r = new(big.Int).SetBytes(rb) 63 | s = new(big.Int).SetBytes(sb) 64 | } 65 | return 66 | } 67 | 68 | type br struct { 69 | buf *bytes.Buffer 70 | err error 71 | } 72 | 73 | func newbr(data []byte) *br { 74 | b := new(br) 75 | b.buf = bytes.NewBuffer(data) 76 | return b 77 | } 78 | 79 | func (b *br) Next() []byte { 80 | if b.err != nil { 81 | return nil 82 | } 83 | 84 | var dlen uint32 85 | b.err = binary.Read(b.buf, binary.BigEndian, &dlen) 86 | if b.err == nil { 87 | if int(dlen) > b.buf.Len() { 88 | return nil 89 | } else if int(dlen) <= 0 { 90 | return nil 91 | } 92 | data := make([]byte, int(dlen)) 93 | b.buf.Read(data) 94 | return data 95 | } 96 | return nil 97 | } 98 | 99 | func (b *br) NextU32() (uint32, bool) { 100 | if b.err != nil { 101 | return 0, false 102 | } 103 | 104 | var n uint32 105 | b.err = binary.Read(b.buf, binary.BigEndian, &n) 106 | if b.err != nil { 107 | return 0, false 108 | } 109 | if int(n) > b.buf.Len() { 110 | return 0, false 111 | } 112 | b.err = binary.Read(b.buf, binary.BigEndian, &n) 113 | return n, b.err == nil 114 | } 115 | 116 | // Zero out a byte slice. 117 | func zero(in []byte) { 118 | if in == nil { 119 | return 120 | } 121 | inlen := len(in) 122 | for i := 0; i < inlen; i++ { 123 | in[i] ^= in[i] 124 | } 125 | } 126 | 127 | // zeroPad returns a new slice of length size. The contents of input are right 128 | // aligned in the new slice. 129 | func zeroPad(in []byte, outlen int) (out []byte) { 130 | var inLen int 131 | if inLen = len(in); inLen > outlen { 132 | inLen = outlen 133 | } else if inLen == outlen { 134 | return in 135 | } 136 | start := outlen - inLen 137 | out = make([]byte, outlen) 138 | copy(out[start:], in) 139 | return 140 | } 141 | -------------------------------------------------------------------------------- /stoutbox/util.go: -------------------------------------------------------------------------------- 1 | package stoutbox 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "math/big" 7 | ) 8 | 9 | const u32Len uint32 = 4 10 | 11 | func marshalSignature(r, s *big.Int) []byte { 12 | sig := newbw(nil) 13 | sig.Write(r.Bytes()) 14 | sig.Write(s.Bytes()) 15 | return sig.Bytes() 16 | } 17 | 18 | type bw struct { 19 | buf *bytes.Buffer 20 | err error 21 | } 22 | 23 | func newbw(init []byte) *bw { 24 | b := new(bw) 25 | b.buf = new(bytes.Buffer) 26 | if init != nil { 27 | b.buf.Write(init) 28 | } 29 | return b 30 | } 31 | 32 | func (b *bw) Write(data []byte) { 33 | if b.err != nil { 34 | return 35 | } 36 | b.err = binary.Write(b.buf, binary.BigEndian, uint32(len(data))) 37 | b.buf.Write(data) 38 | } 39 | 40 | func (b *bw) WriteUint32(n uint32) { 41 | if b.err != nil { 42 | return 43 | } 44 | b.err = binary.Write(b.buf, binary.BigEndian, u32Len) 45 | if b.err == nil { 46 | b.err = binary.Write(b.buf, binary.BigEndian, n) 47 | } 48 | } 49 | 50 | func (b *bw) Bytes() []byte { 51 | if b.err != nil { 52 | return nil 53 | } 54 | return b.buf.Bytes() 55 | } 56 | 57 | func unmarshalSignature(sig []byte) (r, s *big.Int) { 58 | b := newbr(sig) 59 | rb := b.Next() 60 | sb := b.Next() 61 | if rb != nil && sb != nil { 62 | r = new(big.Int).SetBytes(rb) 63 | s = new(big.Int).SetBytes(sb) 64 | } 65 | return 66 | } 67 | 68 | type br struct { 69 | buf *bytes.Buffer 70 | err error 71 | } 72 | 73 | func newbr(data []byte) *br { 74 | b := new(br) 75 | b.buf = bytes.NewBuffer(data) 76 | return b 77 | } 78 | 79 | func (b *br) Next() []byte { 80 | if b.err != nil { 81 | return nil 82 | } 83 | 84 | var dlen uint32 85 | b.err = binary.Read(b.buf, binary.BigEndian, &dlen) 86 | if b.err == nil { 87 | if int(dlen) > b.buf.Len() { 88 | return nil 89 | } else if int(dlen) <= 0 { 90 | return nil 91 | } 92 | data := make([]byte, int(dlen)) 93 | b.buf.Read(data) 94 | return data 95 | } 96 | return nil 97 | } 98 | 99 | func (b *br) NextU32() (uint32, bool) { 100 | if b.err != nil { 101 | return 0, false 102 | } 103 | 104 | var n uint32 105 | b.err = binary.Read(b.buf, binary.BigEndian, &n) 106 | if b.err != nil { 107 | return 0, false 108 | } 109 | if int(n) > b.buf.Len() { 110 | return 0, false 111 | } 112 | b.err = binary.Read(b.buf, binary.BigEndian, &n) 113 | return n, b.err == nil 114 | } 115 | 116 | // Zero out a byte slice. 117 | func zero(in []byte) { 118 | if in == nil { 119 | return 120 | } 121 | inlen := len(in) 122 | for i := 0; i < inlen; i++ { 123 | in[i] ^= in[i] 124 | } 125 | } 126 | 127 | // zeroPad returns a new slice of length size. The contents of input are right 128 | // aligned in the new slice. 129 | func zeroPad(in []byte, outlen int) (out []byte) { 130 | var inLen int 131 | if inLen = len(in); inLen > outlen { 132 | inLen = outlen 133 | } else if inLen == outlen { 134 | return in 135 | } 136 | start := outlen - inLen 137 | out = make([]byte, outlen) 138 | copy(out[start:], in) 139 | return 140 | } 141 | -------------------------------------------------------------------------------- /box/testdata/TEST.txt: -------------------------------------------------------------------------------- 1 | This is the first chapter of Sun Tzu's Art of War, as downloaded from 2 | http://www.gutenberg.org/cache/epub/17405/pg17405.txt. It is included to 3 | provide a large-ish file for encryption and decryption. 4 | 5 | I. LAYING PLANS 6 | 7 | 8 | 1. Sun Tzu said: The art of war is of vital importance 9 | to the State. 10 | 11 | 2. It is a matter of life and death, a road either 12 | to safety or to ruin. Hence it is a subject of inquiry 13 | which can on no account be neglected. 14 | 15 | 3. The art of war, then, is governed by five constant 16 | factors, to be taken into account in one's deliberations, 17 | when seeking to determine the conditions obtaining in the field. 18 | 19 | 4. These are: (1) The Moral Law; (2) Heaven; (3) Earth; 20 | (4) The Commander; (5) Method and discipline. 21 | 22 | 5,6. The Moral Law causes the people to be in complete 23 | accord with their ruler, so that they will follow him 24 | regardless of their lives, undismayed by any danger. 25 | 26 | 7. Heaven signifies night and day, cold and heat, 27 | times and seasons. 28 | 29 | 8. Earth comprises distances, great and small; 30 | danger and security; open ground and narrow passes; 31 | the chances of life and death. 32 | 33 | 9. The Commander stands for the virtues of wisdom, 34 | sincerity, benevolence, courage and strictness. 35 | 36 | 10. By method and discipline are to be understood 37 | the marshaling of the army in its proper subdivisions, 38 | the graduations of rank among the officers, the maintenance 39 | of roads by which supplies may reach the army, and the 40 | control of military expenditure. 41 | 42 | 11. These five heads should be familiar to every general: 43 | he who knows them will be victorious; he who knows them 44 | not will fail. 45 | 46 | 12. Therefore, in your deliberations, when seeking 47 | to determine the military conditions, let them be made 48 | the basis of a comparison, in this wise:-- 49 | 50 | 13. (1) Which of the two sovereigns is imbued 51 | with the Moral law? 52 | (2) Which of the two generals has most ability? 53 | (3) With whom lie the advantages derived from Heaven 54 | and Earth? 55 | (4) On which side is discipline most rigorously enforced? 56 | (5) Which army is stronger? 57 | (6) On which side are officers and men more highly trained? 58 | (7) In which army is there the greater constancy 59 | both in reward and punishment? 60 | 61 | 14. By means of these seven considerations I can 62 | forecast victory or defeat. 63 | 64 | 15. The general that hearkens to my counsel and acts 65 | upon it, will conquer: let such a one be retained in command! 66 | The general that hearkens not to my counsel nor acts upon it, 67 | will suffer defeat:--let such a one be dismissed! 68 | 69 | 16. While heading the profit of my counsel, 70 | avail yourself also of any helpful circumstances 71 | over and beyond the ordinary rules. 72 | 73 | 17. According as circumstances are favorable, 74 | one should modify one's plans. 75 | 76 | 18. All warfare is based on deception. 77 | 78 | 19. Hence, when able to attack, we must seem unable; 79 | when using our forces, we must seem inactive; when we 80 | are near, we must make the enemy believe we are far away; 81 | when far away, we must make him believe we are near. 82 | 83 | 20. Hold out baits to entice the enemy. Feign disorder, 84 | and crush him. 85 | 86 | 21. If he is secure at all points, be prepared for him. 87 | If he is in superior strength, evade him. 88 | 89 | 22. If your opponent is of choleric temper, seek to 90 | irritate him. Pretend to be weak, that he may grow arrogant. 91 | 92 | 23. If he is taking his ease, give him no rest. 93 | If his forces are united, separate them. 94 | 95 | 24. Attack him where he is unprepared, appear where 96 | you are not expected. 97 | 98 | 25. These military devices, leading to victory, 99 | must not be divulged beforehand. 100 | 101 | 26. Now the general who wins a battle makes many 102 | calculations in his temple ere the battle is fought. 103 | The general who loses a battle makes but few 104 | calculations beforehand. Thus do many calculations 105 | lead to victory, and few calculations to defeat: 106 | how much more no calculation at all! It is by attention 107 | to this point that I can foresee who is likely to win or lose. 108 | 109 | -------------------------------------------------------------------------------- /secretbox/testdata/TEST.txt: -------------------------------------------------------------------------------- 1 | This is the first chapter of Sun Tzu's Art of War, as downloaded from 2 | http://www.gutenberg.org/cache/epub/17405/pg17405.txt. It is included to 3 | provide a large-ish file for encryption and decryption. 4 | 5 | I. LAYING PLANS 6 | 7 | 8 | 1. Sun Tzu said: The art of war is of vital importance 9 | to the State. 10 | 11 | 2. It is a matter of life and death, a road either 12 | to safety or to ruin. Hence it is a subject of inquiry 13 | which can on no account be neglected. 14 | 15 | 3. The art of war, then, is governed by five constant 16 | factors, to be taken into account in one's deliberations, 17 | when seeking to determine the conditions obtaining in the field. 18 | 19 | 4. These are: (1) The Moral Law; (2) Heaven; (3) Earth; 20 | (4) The Commander; (5) Method and discipline. 21 | 22 | 5,6. The Moral Law causes the people to be in complete 23 | accord with their ruler, so that they will follow him 24 | regardless of their lives, undismayed by any danger. 25 | 26 | 7. Heaven signifies night and day, cold and heat, 27 | times and seasons. 28 | 29 | 8. Earth comprises distances, great and small; 30 | danger and security; open ground and narrow passes; 31 | the chances of life and death. 32 | 33 | 9. The Commander stands for the virtues of wisdom, 34 | sincerity, benevolence, courage and strictness. 35 | 36 | 10. By method and discipline are to be understood 37 | the marshaling of the army in its proper subdivisions, 38 | the graduations of rank among the officers, the maintenance 39 | of roads by which supplies may reach the army, and the 40 | control of military expenditure. 41 | 42 | 11. These five heads should be familiar to every general: 43 | he who knows them will be victorious; he who knows them 44 | not will fail. 45 | 46 | 12. Therefore, in your deliberations, when seeking 47 | to determine the military conditions, let them be made 48 | the basis of a comparison, in this wise:-- 49 | 50 | 13. (1) Which of the two sovereigns is imbued 51 | with the Moral law? 52 | (2) Which of the two generals has most ability? 53 | (3) With whom lie the advantages derived from Heaven 54 | and Earth? 55 | (4) On which side is discipline most rigorously enforced? 56 | (5) Which army is stronger? 57 | (6) On which side are officers and men more highly trained? 58 | (7) In which army is there the greater constancy 59 | both in reward and punishment? 60 | 61 | 14. By means of these seven considerations I can 62 | forecast victory or defeat. 63 | 64 | 15. The general that hearkens to my counsel and acts 65 | upon it, will conquer: let such a one be retained in command! 66 | The general that hearkens not to my counsel nor acts upon it, 67 | will suffer defeat:--let such a one be dismissed! 68 | 69 | 16. While heading the profit of my counsel, 70 | avail yourself also of any helpful circumstances 71 | over and beyond the ordinary rules. 72 | 73 | 17. According as circumstances are favorable, 74 | one should modify one's plans. 75 | 76 | 18. All warfare is based on deception. 77 | 78 | 19. Hence, when able to attack, we must seem unable; 79 | when using our forces, we must seem inactive; when we 80 | are near, we must make the enemy believe we are far away; 81 | when far away, we must make him believe we are near. 82 | 83 | 20. Hold out baits to entice the enemy. Feign disorder, 84 | and crush him. 85 | 86 | 21. If he is secure at all points, be prepared for him. 87 | If he is in superior strength, evade him. 88 | 89 | 22. If your opponent is of choleric temper, seek to 90 | irritate him. Pretend to be weak, that he may grow arrogant. 91 | 92 | 23. If he is taking his ease, give him no rest. 93 | If his forces are united, separate them. 94 | 95 | 24. Attack him where he is unprepared, appear where 96 | you are not expected. 97 | 98 | 25. These military devices, leading to victory, 99 | must not be divulged beforehand. 100 | 101 | 26. Now the general who wins a battle makes many 102 | calculations in his temple ere the battle is fought. 103 | The general who loses a battle makes but few 104 | calculations beforehand. Thus do many calculations 105 | lead to victory, and few calculations to defeat: 106 | how much more no calculation at all! It is by attention 107 | to this point that I can foresee who is likely to win or lose. 108 | 109 | -------------------------------------------------------------------------------- /stoutbox/testdata/TEST.txt: -------------------------------------------------------------------------------- 1 | This is the first chapter of Sun Tzu's Art of War, as downloaded from 2 | http://www.gutenberg.org/cache/epub/17405/pg17405.txt. It is included to 3 | provide a large-ish file for encryption and decryption. 4 | 5 | I. LAYING PLANS 6 | 7 | 8 | 1. Sun Tzu said: The art of war is of vital importance 9 | to the State. 10 | 11 | 2. It is a matter of life and death, a road either 12 | to safety or to ruin. Hence it is a subject of inquiry 13 | which can on no account be neglected. 14 | 15 | 3. The art of war, then, is governed by five constant 16 | factors, to be taken into account in one's deliberations, 17 | when seeking to determine the conditions obtaining in the field. 18 | 19 | 4. These are: (1) The Moral Law; (2) Heaven; (3) Earth; 20 | (4) The Commander; (5) Method and discipline. 21 | 22 | 5,6. The Moral Law causes the people to be in complete 23 | accord with their ruler, so that they will follow him 24 | regardless of their lives, undismayed by any danger. 25 | 26 | 7. Heaven signifies night and day, cold and heat, 27 | times and seasons. 28 | 29 | 8. Earth comprises distances, great and small; 30 | danger and security; open ground and narrow passes; 31 | the chances of life and death. 32 | 33 | 9. The Commander stands for the virtues of wisdom, 34 | sincerity, benevolence, courage and strictness. 35 | 36 | 10. By method and discipline are to be understood 37 | the marshaling of the army in its proper subdivisions, 38 | the graduations of rank among the officers, the maintenance 39 | of roads by which supplies may reach the army, and the 40 | control of military expenditure. 41 | 42 | 11. These five heads should be familiar to every general: 43 | he who knows them will be victorious; he who knows them 44 | not will fail. 45 | 46 | 12. Therefore, in your deliberations, when seeking 47 | to determine the military conditions, let them be made 48 | the basis of a comparison, in this wise:-- 49 | 50 | 13. (1) Which of the two sovereigns is imbued 51 | with the Moral law? 52 | (2) Which of the two generals has most ability? 53 | (3) With whom lie the advantages derived from Heaven 54 | and Earth? 55 | (4) On which side is discipline most rigorously enforced? 56 | (5) Which army is stronger? 57 | (6) On which side are officers and men more highly trained? 58 | (7) In which army is there the greater constancy 59 | both in reward and punishment? 60 | 61 | 14. By means of these seven considerations I can 62 | forecast victory or defeat. 63 | 64 | 15. The general that hearkens to my counsel and acts 65 | upon it, will conquer: let such a one be retained in command! 66 | The general that hearkens not to my counsel nor acts upon it, 67 | will suffer defeat:--let such a one be dismissed! 68 | 69 | 16. While heading the profit of my counsel, 70 | avail yourself also of any helpful circumstances 71 | over and beyond the ordinary rules. 72 | 73 | 17. According as circumstances are favorable, 74 | one should modify one's plans. 75 | 76 | 18. All warfare is based on deception. 77 | 78 | 19. Hence, when able to attack, we must seem unable; 79 | when using our forces, we must seem inactive; when we 80 | are near, we must make the enemy believe we are far away; 81 | when far away, we must make him believe we are near. 82 | 83 | 20. Hold out baits to entice the enemy. Feign disorder, 84 | and crush him. 85 | 86 | 21. If he is secure at all points, be prepared for him. 87 | If he is in superior strength, evade him. 88 | 89 | 22. If your opponent is of choleric temper, seek to 90 | irritate him. Pretend to be weak, that he may grow arrogant. 91 | 92 | 23. If he is taking his ease, give him no rest. 93 | If his forces are united, separate them. 94 | 95 | 24. Attack him where he is unprepared, appear where 96 | you are not expected. 97 | 98 | 25. These military devices, leading to victory, 99 | must not be divulged beforehand. 100 | 101 | 26. Now the general who wins a battle makes many 102 | calculations in his temple ere the battle is fought. 103 | The general who loses a battle makes but few 104 | calculations beforehand. Thus do many calculations 105 | lead to victory, and few calculations to defeat: 106 | how much more no calculation at all! It is by attention 107 | to this point that I can foresee who is likely to win or lose. 108 | 109 | -------------------------------------------------------------------------------- /strongbox/testdata/TEST.txt: -------------------------------------------------------------------------------- 1 | This is the first chapter of Sun Tzu's Art of War, as downloaded from 2 | http://www.gutenberg.org/cache/epub/17405/pg17405.txt. It is included to 3 | provide a large-ish file for encryption and decryption. 4 | 5 | I. LAYING PLANS 6 | 7 | 8 | 1. Sun Tzu said: The art of war is of vital importance 9 | to the State. 10 | 11 | 2. It is a matter of life and death, a road either 12 | to safety or to ruin. Hence it is a subject of inquiry 13 | which can on no account be neglected. 14 | 15 | 3. The art of war, then, is governed by five constant 16 | factors, to be taken into account in one's deliberations, 17 | when seeking to determine the conditions obtaining in the field. 18 | 19 | 4. These are: (1) The Moral Law; (2) Heaven; (3) Earth; 20 | (4) The Commander; (5) Method and discipline. 21 | 22 | 5,6. The Moral Law causes the people to be in complete 23 | accord with their ruler, so that they will follow him 24 | regardless of their lives, undismayed by any danger. 25 | 26 | 7. Heaven signifies night and day, cold and heat, 27 | times and seasons. 28 | 29 | 8. Earth comprises distances, great and small; 30 | danger and security; open ground and narrow passes; 31 | the chances of life and death. 32 | 33 | 9. The Commander stands for the virtues of wisdom, 34 | sincerity, benevolence, courage and strictness. 35 | 36 | 10. By method and discipline are to be understood 37 | the marshaling of the army in its proper subdivisions, 38 | the graduations of rank among the officers, the maintenance 39 | of roads by which supplies may reach the army, and the 40 | control of military expenditure. 41 | 42 | 11. These five heads should be familiar to every general: 43 | he who knows them will be victorious; he who knows them 44 | not will fail. 45 | 46 | 12. Therefore, in your deliberations, when seeking 47 | to determine the military conditions, let them be made 48 | the basis of a comparison, in this wise:-- 49 | 50 | 13. (1) Which of the two sovereigns is imbued 51 | with the Moral law? 52 | (2) Which of the two generals has most ability? 53 | (3) With whom lie the advantages derived from Heaven 54 | and Earth? 55 | (4) On which side is discipline most rigorously enforced? 56 | (5) Which army is stronger? 57 | (6) On which side are officers and men more highly trained? 58 | (7) In which army is there the greater constancy 59 | both in reward and punishment? 60 | 61 | 14. By means of these seven considerations I can 62 | forecast victory or defeat. 63 | 64 | 15. The general that hearkens to my counsel and acts 65 | upon it, will conquer: let such a one be retained in command! 66 | The general that hearkens not to my counsel nor acts upon it, 67 | will suffer defeat:--let such a one be dismissed! 68 | 69 | 16. While heading the profit of my counsel, 70 | avail yourself also of any helpful circumstances 71 | over and beyond the ordinary rules. 72 | 73 | 17. According as circumstances are favorable, 74 | one should modify one's plans. 75 | 76 | 18. All warfare is based on deception. 77 | 78 | 19. Hence, when able to attack, we must seem unable; 79 | when using our forces, we must seem inactive; when we 80 | are near, we must make the enemy believe we are far away; 81 | when far away, we must make him believe we are near. 82 | 83 | 20. Hold out baits to entice the enemy. Feign disorder, 84 | and crush him. 85 | 86 | 21. If he is secure at all points, be prepared for him. 87 | If he is in superior strength, evade him. 88 | 89 | 22. If your opponent is of choleric temper, seek to 90 | irritate him. Pretend to be weak, that he may grow arrogant. 91 | 92 | 23. If he is taking his ease, give him no rest. 93 | If his forces are united, separate them. 94 | 95 | 24. Attack him where he is unprepared, appear where 96 | you are not expected. 97 | 98 | 25. These military devices, leading to victory, 99 | must not be divulged beforehand. 100 | 101 | 26. Now the general who wins a battle makes many 102 | calculations in his temple ere the battle is fought. 103 | The general who loses a battle makes but few 104 | calculations beforehand. Thus do many calculations 105 | lead to victory, and few calculations to defeat: 106 | how much more no calculation at all! It is by attention 107 | to this point that I can foresee who is likely to win or lose. 108 | 109 | -------------------------------------------------------------------------------- /secretbox/secretbox.go: -------------------------------------------------------------------------------- 1 | /* 2 | secretbox is used to authenticate and secure small messages. It 3 | provides an interface similar to NaCL, but uses AES-128 in CTR 4 | mode with HMAC-SHA-256 for securing messages. 5 | 6 | Messages should be secured using the Seal function, and recovered 7 | using the Open function. A box (or authenticated and encrypted 8 | message) will be Overhead bytes longer than the message it 9 | came from; this package will not obscure the length of the 10 | message. Keys, if they are not generated using the GenerateKey 11 | function, should be KeySize bytes long. The KeyIsSuitable function 12 | may be used to test a key is the proper length. 13 | 14 | The boxes used in this package are suitable for 20-year security. 15 | */ 16 | package secretbox 17 | 18 | import ( 19 | "crypto/aes" 20 | "crypto/cipher" 21 | "crypto/hmac" 22 | "crypto/rand" 23 | "crypto/sha256" 24 | "crypto/subtle" 25 | "fmt" 26 | "io" 27 | ) 28 | 29 | const cryptKeySize = 16 30 | const tagKeySize = 32 31 | 32 | const VersionString = "2.0.0" 33 | 34 | // KeySize is the number of bytes a valid key should be. 35 | const KeySize = cryptKeySize + tagKeySize 36 | 37 | // Overhead is the number of bytes of overhead when boxing a message. 38 | const Overhead = aes.BlockSize + sha256.Size 39 | 40 | var ( 41 | errinvalidKeySize = fmt.Errorf("invalid key size") 42 | errinvalidCiphertext = fmt.Errorf("invalid ciphertext") 43 | ) 44 | 45 | // The default source for random data is the crypto/rand package's Reader. 46 | var PRNG = rand.Reader 47 | 48 | type Key []byte 49 | type nonce []byte 50 | 51 | // GenerateKey returns a key suitable for sealing and opening boxes, and 52 | // a boolean indicating success. If the boolean returns false, the Key 53 | // value must be discarded. 54 | func GenerateKey() (Key, bool) { 55 | var key Key = make([]byte, KeySize) 56 | 57 | _, err := io.ReadFull(PRNG, key) 58 | return key, err == nil 59 | } 60 | 61 | func generateNonce() (nonce, error) { 62 | var n nonce = make([]byte, aes.BlockSize) 63 | 64 | _, err := io.ReadFull(PRNG, n) 65 | return n, err 66 | } 67 | 68 | func encrypt(key []byte, in []byte) (out []byte, err error) { 69 | var iv nonce 70 | if iv, err = generateNonce(); err != nil { 71 | return 72 | } 73 | 74 | out = make([]byte, len(in)+aes.BlockSize) 75 | for i := 0; i < aes.BlockSize; i++ { 76 | out[i] = iv[i] 77 | iv[i] = 0 78 | } 79 | 80 | c, err := aes.NewCipher(key) 81 | if err != nil { 82 | return 83 | } 84 | ctr := cipher.NewCTR(c, out[:aes.BlockSize]) 85 | ctr.XORKeyStream(out[aes.BlockSize:], in) 86 | return 87 | } 88 | 89 | func computeTag(key []byte, in []byte) (tag []byte) { 90 | h := hmac.New(sha256.New, key) 91 | h.Write(in) 92 | return h.Sum(nil) 93 | } 94 | 95 | func checkTag(key, in []byte) bool { 96 | ctlen := len(in) - sha256.Size 97 | tag := in[ctlen:] 98 | ct := in[:ctlen] 99 | actualTag := computeTag(key, ct) 100 | return subtle.ConstantTimeCompare(tag, actualTag) == 1 101 | } 102 | 103 | func decrypt(key []byte, in []byte) (out []byte, err error) { 104 | if len(in) < aes.BlockSize { 105 | return nil, errinvalidCiphertext 106 | } 107 | 108 | c, err := aes.NewCipher(key) 109 | if err != nil { 110 | return 111 | } 112 | 113 | iv := in[:aes.BlockSize] 114 | ct := in[aes.BlockSize:] 115 | ctr := cipher.NewCTR(c, iv) 116 | out = make([]byte, len(ct)) 117 | ctr.XORKeyStream(out, ct) 118 | return 119 | } 120 | 121 | // Seal returns an authenticated and encrypted message, and a boolean 122 | // indicating whether the sealing operation was successful. If it returns 123 | // true, the message was successfully sealed. The box will be Overhead 124 | // bytes longer than the message. 125 | func Seal(message []byte, key Key) (box []byte, ok bool) { 126 | if !KeyIsSuitable(key) { 127 | return 128 | } 129 | 130 | ct, err := encrypt(key[:cryptKeySize], message) 131 | if err != nil { 132 | return 133 | } 134 | tag := computeTag(key[cryptKeySize:], ct) 135 | box = append(ct, tag...) 136 | ok = true 137 | return 138 | } 139 | 140 | // Open authenticates and decrypts a sealed message, also returning 141 | // whether the message was successfully opened. If this is false, the 142 | // message must be discarded. The returned message will be Overhead 143 | // bytes shorter than the box. 144 | func Open(box []byte, key Key) (message []byte, ok bool) { 145 | if !KeyIsSuitable(key) { 146 | return 147 | } else if box == nil { 148 | return 149 | } else if len(box) < Overhead { 150 | return 151 | } 152 | 153 | msgLen := len(box) - sha256.Size 154 | if !checkTag(key[cryptKeySize:], box) { 155 | return nil, ok 156 | } 157 | message, err := decrypt(key[:cryptKeySize], box[:msgLen]) 158 | ok = err == nil 159 | return 160 | } 161 | 162 | // IsKeySuitable returns true if the byte slice represents a valid 163 | // secretbox key. 164 | func KeyIsSuitable(key []byte) bool { 165 | return subtle.ConstantTimeEq(int32(len(key)), int32(KeySize)) == 1 166 | } 167 | -------------------------------------------------------------------------------- /strongbox/strongbox.go: -------------------------------------------------------------------------------- 1 | /* 2 | strongbox is used to authenticate and secure small messages. It 3 | provides an interface similar to NaCL, but uses AES-256 with 4 | HMAC-SHA-384 for securing messages. 5 | 6 | Messages should be secured using the Seal function, and recovered 7 | using the Open function. A box (or authenticated and encrypted 8 | message) will be Overhead bytes longer than the message it 9 | came from; this package will not obscure the length of the 10 | message. Keys, if they are not generated using the GenerateKey 11 | function, should be KeySize bytes long. The KeyIsSuitable function 12 | may be used to test a key is the proper length. 13 | 14 | The boxes used in this package are suitable for 50-year security, 15 | assuming the keys are not compromised. 16 | */ 17 | package strongbox 18 | 19 | import ( 20 | "crypto/aes" 21 | "crypto/cipher" 22 | "crypto/hmac" 23 | "crypto/rand" 24 | "crypto/sha512" 25 | "crypto/subtle" 26 | "fmt" 27 | "io" 28 | ) 29 | 30 | const cryptKeySize = 32 31 | const tagKeySize = 48 32 | 33 | const VersionString = "2.0.0" 34 | 35 | // KeySize is the number of bytes a valid key should be. 36 | const KeySize = cryptKeySize + tagKeySize 37 | 38 | // Overhead is the number of bytes of overhead when boxing a message. 39 | const Overhead = aes.BlockSize + sha512.Size384 40 | 41 | var ( 42 | errinvalidKeySize = fmt.Errorf("invalid key size") 43 | errinvalidCiphertext = fmt.Errorf("invalid ciphertext") 44 | ) 45 | 46 | // The default source for random data is the crypto/rand package's Reader. 47 | var PRNG = rand.Reader 48 | 49 | type Key []byte 50 | type nonce []byte 51 | 52 | // GenerateKey returns a key suitable for sealing and opening boxes, and a 53 | // boolean indicating success. If the boolean is false, the Key value must 54 | // be discarded. 55 | func GenerateKey() (Key, bool) { 56 | var key Key = make([]byte, KeySize) 57 | 58 | _, err := io.ReadFull(PRNG, key) 59 | return key, err == nil 60 | } 61 | 62 | func generateNonce() (nonce, error) { 63 | var n nonce = make([]byte, aes.BlockSize) 64 | 65 | _, err := io.ReadFull(PRNG, n) 66 | return n, err 67 | 68 | } 69 | 70 | func encrypt(key []byte, in []byte) (out []byte, err error) { 71 | var iv nonce 72 | if iv, err = generateNonce(); err != nil { 73 | return 74 | } 75 | 76 | out = make([]byte, len(in)+aes.BlockSize) 77 | for i := 0; i < aes.BlockSize; i++ { 78 | out[i] = iv[i] 79 | iv[i] = 0 80 | } 81 | 82 | c, err := aes.NewCipher(key) 83 | if err != nil { 84 | return 85 | } 86 | ctr := cipher.NewCTR(c, out[:aes.BlockSize]) 87 | ctr.XORKeyStream(out[aes.BlockSize:], in) 88 | return 89 | } 90 | 91 | func computeTag(key []byte, in []byte) (tag []byte) { 92 | h := hmac.New(sha512.New384, key) 93 | h.Write(in) 94 | return h.Sum(nil) 95 | } 96 | 97 | func checkTag(key, in []byte) bool { 98 | ctlen := len(in) - sha512.Size384 99 | tag := in[ctlen:] 100 | ct := in[:ctlen] 101 | actualTag := computeTag(key, ct) 102 | return subtle.ConstantTimeCompare(tag, actualTag) == 1 103 | } 104 | 105 | func decrypt(key []byte, in []byte) (out []byte, err error) { 106 | if len(in) < aes.BlockSize { 107 | return nil, errinvalidCiphertext 108 | } 109 | 110 | c, err := aes.NewCipher(key) 111 | if err != nil { 112 | return 113 | } 114 | 115 | iv := in[:aes.BlockSize] 116 | ct := in[aes.BlockSize:] 117 | ctr := cipher.NewCTR(c, iv) 118 | out = make([]byte, len(ct)) 119 | ctr.XORKeyStream(out, ct) 120 | return 121 | } 122 | 123 | // Seal returns an authenticated and encrypted message, and a boolean 124 | // indicating whether the sealing operation was successful. If it returns 125 | //true, the message was successfully sealed. The box will be Overhead 126 | // bytes longer than the message. 127 | func Seal(message []byte, key Key) (box []byte, ok bool) { 128 | if !KeyIsSuitable(key) { 129 | return 130 | } 131 | 132 | ct, err := encrypt(key[:cryptKeySize], message) 133 | if err != nil { 134 | return 135 | } 136 | tag := computeTag(key[cryptKeySize:], ct) 137 | box = append(ct, tag...) 138 | ok = true 139 | return 140 | } 141 | 142 | // Open authenticates and decrypts a sealed message, also returning 143 | // whether the message was successfully opened. If this is false, the 144 | // message must be discarded. The returned message will be Overhead 145 | // bytes shorter than the box. 146 | func Open(box []byte, key Key) (message []byte, ok bool) { 147 | if !KeyIsSuitable(key) { 148 | return 149 | } else if len(box) < Overhead { 150 | return 151 | } 152 | 153 | msgLen := len(box) - sha512.Size384 154 | if !checkTag(key[cryptKeySize:], box) { 155 | return nil, false 156 | } 157 | message, err := decrypt(key[:cryptKeySize], box[:msgLen]) 158 | ok = err == nil 159 | return 160 | } 161 | 162 | // IsKeySuitable returns true if the byte slice represents a valid 163 | // secretbox key. 164 | func KeyIsSuitable(key []byte) bool { 165 | return subtle.ConstantTimeEq(int32(len(key)), int32(KeySize)) == 1 166 | } 167 | -------------------------------------------------------------------------------- /secretbox/secretbox_test.go: -------------------------------------------------------------------------------- 1 | package secretbox 2 | 3 | import "bytes" 4 | import "crypto/rand" 5 | import "fmt" 6 | import "io/ioutil" 7 | import "math/big" 8 | import "testing" 9 | 10 | var testMessages = []string{ 11 | "Hello, world.", 12 | "Yes... yes. This is a fertile land, and we will thrive. We will rule over all this land, and we will call it... This Land.", 13 | "Ah! Curse your sudden but inevitable betrayal!", 14 | "And I'm thinkin' you weren't burdened with an overabundance of schooling. So why don't we just ignore each other until we go away?", 15 | "Sir, I think you have a problem with your brain being missing.", 16 | "It's the way of life in my findings that journeys end when and where they want to; and that's where you make your home.", 17 | "I get confused. I remember everything. I remember too much. And... some of it's made up, and... some of it can't be quantified, and... there's secrets... and...", 18 | "Yeah, we're pretty much just giving each other significant glances and laughing incessantly.", 19 | "Jayne, go play with your rainstick.", 20 | } 21 | 22 | var ( 23 | testBoxes = make([]string, len(testMessages)) 24 | testBoxFile []byte 25 | testGoodKey = Key{ 26 | 0x67, 0xfc, 0x79, 0x46, 0xd6, 0xbf, 0xdc, 0xde, 27 | 0x0c, 0xe3, 0x21, 0xea, 0xda, 0x02, 0xf9, 0xe5, 28 | 0x18, 0xb2, 0x3a, 0xd9, 0xe8, 0xa3, 0x3b, 0x20, 29 | 0x0f, 0xda, 0x96, 0xe6, 0x91, 0x78, 0x91, 0x1f, 30 | 0x69, 0x0a, 0x3c, 0xae, 0xa0, 0xf4, 0x5a, 0x82, 31 | 0xd1, 0x07, 0xa0, 0x7a, 0x78, 0x8d, 0xeb, 0x54, 32 | } 33 | testBadKey = Key{ 34 | 0xe2, 0xbb, 0x58, 0x48, 0xba, 0x2a, 0x0c, 0xd0, 35 | 0x07, 0x3d, 0x32, 0xdb, 0x3a, 0xeb, 0x1b, 0x5b, 36 | 0x36, 0x0f, 0xd0, 0x8f, 0x1a, 0xa0, 0x77, 0x93, 37 | 0x7d, 0x0d, 0xd6, 0x38, 0x57, 0xe6, 0x80, 0xcb, 38 | 0xae, 0x7b, 0x46, 0x2e, 0xe5, 0x5b, 0x5a, 0x26, 39 | 0x60, 0xf2, 0x26, 0x80, 0xfa, 0xb2, 0x30, 0xf8, 40 | } 41 | ) 42 | 43 | func randInt(max int64) int64 { 44 | maxBig := big.NewInt(max) 45 | n, err := rand.Int(PRNG, maxBig) 46 | if err != nil { 47 | return -1 48 | } 49 | return n.Int64() 50 | } 51 | 52 | func mutate(in []byte) (out []byte) { 53 | out = make([]byte, len(in)) 54 | copy(out, in) 55 | 56 | iterations := (randInt(int64(len(out))) / 2) + 1 57 | if iterations == -1 { 58 | panic("mutate failed") 59 | } 60 | for i := 0; i < int(iterations); i++ { 61 | mByte := randInt(int64(len(out))) 62 | mBit := randInt(7) 63 | if mBit == -1 || mByte == -1 { 64 | panic("mutate failed") 65 | } 66 | out[mByte] ^= (1 << uint(mBit)) 67 | } 68 | if bytes.Equal(out, in) { 69 | panic("mutate failed") 70 | } 71 | return out 72 | } 73 | 74 | // TestKeyGeneration generates a pair of keys, verifying that the key 75 | // generation code works properly. 76 | /* 77 | func TestKeyGeneration(t *testing.T) { 78 | var err error 79 | testGoodKey, err = GenerateKey() 80 | if err != nil { 81 | fmt.Println("Failed to generate key:", err.Error()) 82 | t.FailNow() 83 | } 84 | testBadKey, err = GenerateKey() 85 | if err != nil { 86 | fmt.Println("Failed to generate key:", err.Error()) 87 | t.FailNow() 88 | } 89 | ioutil.WriteFile("testvectors/good.key", testGoodKey, 0644) 90 | ioutil.WriteFile("testvectors/bad.key", testBadKey, 0644) 91 | } 92 | */ 93 | 94 | /* 95 | func TestBasicUnbox(t *testing.T) { 96 | testMessage := []byte{1, 2, 3, 4, 5} 97 | testKey := []byte{ 98 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 99 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 100 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 101 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 102 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 103 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 104 | } 105 | box, ok := Seal(testMessage, testKey) 106 | if !ok { 107 | fmt.Println("[+] basic unboxing failed!") 108 | t.FailNow() 109 | } 110 | } 111 | */ 112 | 113 | // TestBoxing ensures that sealing a message into a box works properly. 114 | func TestBoxing(t *testing.T) { 115 | for i := 0; i < len(testMessages); i++ { 116 | box, ok := Seal([]byte(testMessages[i]), testGoodKey) 117 | if !ok { 118 | fmt.Println("Boxing failed: message", i) 119 | t.FailNow() 120 | } else if len(box) != len(testMessages[i])+Overhead { 121 | fmt.Println("The box length is invalid.") 122 | t.FailNow() 123 | } 124 | /* 125 | fmt.Printf("vector[%d]: %x\n", i, box) 126 | fileName := fmt.Sprintf("testvectors/test_box-%d.bin", i+1) 127 | ioutil.WriteFile(fileName, box, 0644) 128 | fileName = fmt.Sprintf("testvectors/test_vector-%d.bin", i+1) 129 | ioutil.WriteFile(fileName, []byte(testMessages[i]), 0644) 130 | */ 131 | testBoxes[i] = string(box) 132 | } 133 | } 134 | 135 | // TestUnboxing ensures that unsealing (or opening) a box to retrieve 136 | // a message works properly. 137 | func TestUnboxing(t *testing.T) { 138 | for i := 0; i < len(testMessages); i++ { 139 | message, ok := Open([]byte(testBoxes[i]), testGoodKey) 140 | if !ok { 141 | fmt.Println("Unboxing failed: message", i) 142 | t.FailNow() 143 | } else if string(message) != testMessages[i] { 144 | fmt.Printf("Unboxing failed: expected '%s', got '%s'\n", 145 | testMessages[i], string(message)) 146 | t.FailNow() 147 | } 148 | } 149 | } 150 | 151 | // TestEmptyBox validates the behaviour of an empty box. 152 | func TestEmptyBox(t *testing.T) { 153 | var msg = []byte{} 154 | 155 | box, ok := Seal(msg, testGoodKey) 156 | if !ok { 157 | t.Fatal("secretbox: failed to seal message") 158 | } 159 | 160 | out, ok := Open(box, testGoodKey) 161 | if !ok { 162 | t.Fatal("secretbox: failed to open message") 163 | } else if !bytes.Equal(out, msg) { 164 | t.Fatal("secretbox: output message doesn't match original") 165 | } 166 | } 167 | 168 | // TestUnboxingFails ensures that attempting to retrieve a message from 169 | // a box with the wrong key will fail. 170 | func TestUnboxingFails(t *testing.T) { 171 | for i := 0; i < len(testMessages); i++ { 172 | _, ok := Open([]byte(testBoxes[i]), testBadKey) 173 | if ok { 174 | fmt.Println("Unboxing should have failed with bad key:", i) 175 | t.FailNow() 176 | } 177 | _, ok = Open(mutate([]byte(testBoxes[i])), testGoodKey) 178 | if ok { 179 | fmt.Println("Modified message should have failed:", i) 180 | t.FailNow() 181 | } 182 | } 183 | } 184 | 185 | // TestLargerBox tests the encryption of a 4,026 byte test file. 186 | func TestLargerBox(t *testing.T) { 187 | var err error 188 | testBoxFile, err = ioutil.ReadFile("testdata/TEST.txt") 189 | if err != nil { 190 | fmt.Println("Failed to read test data:", err.Error()) 191 | t.FailNow() 192 | } 193 | 194 | box, ok := Seal(testBoxFile, testGoodKey) 195 | if !ok { 196 | fmt.Println("Failed to box message.") 197 | t.FailNow() 198 | } 199 | 200 | message, ok := Open(box, testGoodKey) 201 | if !ok { 202 | fmt.Println("Failed to unbox message.") 203 | t.FailNow() 204 | } 205 | 206 | if !bytes.Equal(message, testBoxFile) { 207 | fmt.Println("Recovered message is invalid.") 208 | t.FailNow() 209 | } 210 | } 211 | 212 | // Benchmark the Seal function, which secures the message. 213 | func BenchmarkSeal(b *testing.B) { 214 | for i := 0; i < b.N; i++ { 215 | _, ok := Seal(testBoxFile, testGoodKey) 216 | if !ok { 217 | fmt.Println("Couldn't seal message: benchmark aborted.") 218 | b.FailNow() 219 | } 220 | } 221 | } 222 | 223 | // Benchmark the Open function, which retrieves a message from a box. 224 | func BenchmarkOpen(b *testing.B) { 225 | box, ok := Seal(testBoxFile, testGoodKey) 226 | if !ok { 227 | fmt.Println("Can't seal message: benchmark aborted.") 228 | b.FailNow() 229 | } 230 | for i := 0; i < b.N; i++ { 231 | _, ok := Open(box, testGoodKey) 232 | if !ok { 233 | fmt.Println("Couldn't open message: benchmark aborted.") 234 | b.FailNow() 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /strongbox/strongbox_test.go: -------------------------------------------------------------------------------- 1 | package strongbox 2 | 3 | import "bytes" 4 | import "crypto/rand" 5 | import "fmt" 6 | import "io/ioutil" 7 | import "math/big" 8 | import "testing" 9 | 10 | var testMessages = []string{ 11 | "Hello, world.", 12 | "Yes... yes. This is a fertile land, and we will thrive. We will rule over all this land, and we will call it... This Land.", 13 | "Ah! Curse your sudden but inevitable betrayal!", 14 | "And I'm thinkin' you weren't burdened with an overabundance of schooling. So why don't we just ignore each other until we go away?", 15 | "Sir, I think you have a problem with your brain being missing.", 16 | "It's the way of life in my findings that journeys end when and where they want to; and that's where you make your home.", 17 | "I get confused. I remember everything. I remember too much. And... some of it's made up, and... some of it can't be quantified, and... there's secrets... and...", 18 | "Yeah, we're pretty much just giving each other significant glances and laughing incessantly.", 19 | "Jayne, go play with your rainstick.", 20 | } 21 | 22 | var ( 23 | testBoxes = make([]string, len(testMessages)) 24 | testBoxFile []byte 25 | testGoodKey = Key{ 26 | 0x7c, 0xde, 0x21, 0xcf, 0x13, 0x1d, 0x45, 0x40, 27 | 0x81, 0x16, 0xda, 0x93, 0xe0, 0x04, 0x78, 0x0d, 28 | 0x71, 0x16, 0x15, 0x4c, 0x80, 0x02, 0x9b, 0x20, 29 | 0x7c, 0xb4, 0x31, 0x50, 0x5b, 0xf9, 0x25, 0x1a, 30 | 0x71, 0x01, 0x43, 0xc1, 0x0d, 0xc9, 0xc7, 0x04, 31 | 0xf6, 0xb9, 0x26, 0x80, 0xde, 0xd2, 0xc5, 0x78, 32 | 0xd9, 0x7d, 0x66, 0xd2, 0x3b, 0x7a, 0x20, 0xd0, 33 | 0xdc, 0x90, 0xa0, 0x30, 0x43, 0x8a, 0x45, 0x82, 34 | 0x9f, 0x08, 0x16, 0x96, 0xd4, 0x24, 0x1d, 0xd3, 35 | 0xea, 0x2a, 0x92, 0x4c, 0x8b, 0x89, 0xad, 0xb6, 36 | } 37 | testBadKey = Key{ 38 | 0xe1, 0x56, 0xea, 0x9d, 0x67, 0x90, 0xe2, 0x68, 39 | 0xb3, 0x83, 0x5c, 0x1e, 0x02, 0x62, 0xc7, 0x06, 40 | 0xcb, 0xee, 0xf7, 0x18, 0xd3, 0x7c, 0x41, 0x40, 41 | 0xf8, 0xcc, 0x34, 0xfd, 0xcc, 0xfd, 0x21, 0x0b, 42 | 0x31, 0xee, 0x82, 0xc4, 0x71, 0xa4, 0xb9, 0xeb, 43 | 0xef, 0xed, 0x8c, 0x84, 0xc7, 0xcd, 0x17, 0xc9, 44 | 0x76, 0xf2, 0x57, 0xe5, 0x63, 0x3c, 0x0e, 0xe4, 45 | 0xdb, 0x54, 0x3f, 0xb3, 0xd3, 0x97, 0xf9, 0xf1, 46 | 0x9d, 0x36, 0xc9, 0xbf, 0x58, 0xd8, 0x15, 0x56, 47 | 0x07, 0xe6, 0x1e, 0xcf, 0x51, 0xb4, 0xde, 0xe8, 48 | } 49 | ) 50 | 51 | func randInt(max int64) int64 { 52 | maxBig := big.NewInt(max) 53 | n, err := rand.Int(PRNG, maxBig) 54 | if err != nil { 55 | return -1 56 | } 57 | return n.Int64() 58 | } 59 | 60 | func mutate(in []byte) (out []byte) { 61 | out = make([]byte, len(in)) 62 | copy(out, in) 63 | 64 | iterations := (randInt(int64(len(out))) / 2) + 1 65 | if iterations == -1 { 66 | panic("mutate failed") 67 | } 68 | for i := 0; i < int(iterations); i++ { 69 | mByte := randInt(int64(len(out))) 70 | mBit := randInt(7) 71 | if mBit == -1 || mByte == -1 { 72 | panic("mutate failed") 73 | } 74 | out[mByte] ^= (1 << uint(mBit)) 75 | } 76 | if bytes.Equal(out, in) { 77 | panic("mutate failed") 78 | } 79 | return out 80 | } 81 | 82 | // TestKeyGeneration generates a pair of keys, verifying that the key 83 | // generation code works properly. 84 | /* 85 | func TestKeyGeneration(t *testing.T) { 86 | var err error 87 | testGoodKey, err = GenerateKey() 88 | if err != nil { 89 | fmt.Println("Failed to generate key:", err.Error()) 90 | t.FailNow() 91 | } 92 | testBadKey, err = GenerateKey() 93 | if err != nil { 94 | fmt.Println("Failed to generate key:", err.Error()) 95 | t.FailNow() 96 | } 97 | ioutil.WriteFile("testvectors/good.key", testGoodKey, 0644) 98 | ioutil.WriteFile("testvectors/bad.key", testBadKey, 0644) 99 | } 100 | */ 101 | 102 | /* 103 | func TestBasicUnbox(t *testing.T) { 104 | testMessage := []byte{1, 2, 3, 4, 5} 105 | testKey := []byte{ 106 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 107 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 108 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 109 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 110 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 111 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 112 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 113 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 114 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 115 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 116 | } 117 | box, ok := Seal(testMessage, testKey) 118 | if !ok { 119 | fmt.Println("[+] basic unboxing failed!") 120 | t.FailNow() 121 | } 122 | } 123 | */ 124 | 125 | // TestBoxing ensures that sealing a message into a box works properly. 126 | func TestBoxing(t *testing.T) { 127 | for i := 0; i < len(testMessages); i++ { 128 | box, ok := Seal([]byte(testMessages[i]), testGoodKey) 129 | if !ok { 130 | fmt.Println("Boxing failed: message", i) 131 | t.FailNow() 132 | } else if len(box) != len(testMessages[i])+Overhead { 133 | fmt.Println("The box length is invalid.") 134 | t.FailNow() 135 | } 136 | testBoxes[i] = string(box) 137 | } 138 | } 139 | 140 | // TestUnboxing ensures that unsealing (or opening) a box to retrieve 141 | // a message works properly. 142 | func TestUnboxing(t *testing.T) { 143 | for i := 0; i < len(testMessages); i++ { 144 | message, ok := Open([]byte(testBoxes[i]), testGoodKey) 145 | if !ok { 146 | fmt.Println("Unboxing failed: message", i) 147 | t.FailNow() 148 | } else if string(message) != testMessages[i] { 149 | fmt.Printf("Unboxing failed: expected '%s', got '%s'\n", 150 | testMessages[i], string(message)) 151 | t.FailNow() 152 | } 153 | } 154 | } 155 | 156 | // TestUnboxingFails ensures that attempting to retrieve a message from 157 | // a box with the wrong key will fail. 158 | func TestUnboxingFails(t *testing.T) { 159 | for i := 0; i < len(testMessages); i++ { 160 | _, ok := Open([]byte(testBoxes[i]), testBadKey) 161 | if ok { 162 | fmt.Println("Unboxing should have failed with bad key:", i) 163 | t.FailNow() 164 | } 165 | _, ok = Open(mutate([]byte(testBoxes[i])), testGoodKey) 166 | if ok { 167 | fmt.Println("Modified message should have failed:", i) 168 | t.FailNow() 169 | } 170 | } 171 | } 172 | 173 | // TestLargerBox tests the encryption of a 4,026 byte test file. 174 | func TestLargerBox(t *testing.T) { 175 | var err error 176 | testBoxFile, err = ioutil.ReadFile("testdata/TEST.txt") 177 | if err != nil { 178 | fmt.Println("Failed to read test data:", err.Error()) 179 | t.FailNow() 180 | } 181 | 182 | box, ok := Seal(testBoxFile, testGoodKey) 183 | if !ok { 184 | fmt.Println("Failed to box message.") 185 | t.FailNow() 186 | } 187 | 188 | message, ok := Open(box, testGoodKey) 189 | if !ok { 190 | fmt.Println("Failed to unbox message.") 191 | t.FailNow() 192 | } 193 | 194 | if !bytes.Equal(message, testBoxFile) { 195 | fmt.Println("Recovered message is invalid.") 196 | t.FailNow() 197 | } 198 | } 199 | 200 | // TestEmptyBox validates the behaviour of an empty box. 201 | func TestEmptyBox(t *testing.T) { 202 | var msg = []byte{} 203 | 204 | box, ok := Seal(msg, testGoodKey) 205 | if !ok { 206 | t.Fatal("secretbox: failed to seal message") 207 | } 208 | 209 | out, ok := Open(box, testGoodKey) 210 | if !ok { 211 | t.Fatal("secretbox: failed to open message") 212 | } else if !bytes.Equal(out, msg) { 213 | t.Fatal("secretbox: output message doesn't match original") 214 | } 215 | } 216 | 217 | // Benchmark the Seal function, which secures the message. 218 | func BenchmarkSeal(b *testing.B) { 219 | for i := 0; i < b.N; i++ { 220 | _, ok := Seal(testBoxFile, testGoodKey) 221 | if !ok { 222 | fmt.Println("Couldn't seal message: benchmark aborted.") 223 | b.FailNow() 224 | } 225 | } 226 | } 227 | 228 | // Benchmark the Open function, which retrieves a message from a box. 229 | func BenchmarkOpen(b *testing.B) { 230 | box, ok := Seal(testBoxFile, testGoodKey) 231 | if !ok { 232 | fmt.Println("Can't seal message: benchmark aborted.") 233 | b.FailNow() 234 | } 235 | for i := 0; i < b.N; i++ { 236 | _, ok := Open(box, testGoodKey) 237 | if !ok { 238 | fmt.Println("Couldn't open message: benchmark aborted.") 239 | b.FailNow() 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /box/box.go: -------------------------------------------------------------------------------- 1 | /* 2 | box is used to authenticate and secure messages using public-key 3 | cryptography. It provides an interface similar to NaCL, but uses 4 | ECIES using ephemeral ECDH for shared keys, and secret box for 5 | securing messages. 6 | 7 | Messages should be secured using the Seal function, and recovered 8 | using the Open function. A box (or authenticated and encrypted 9 | message) will be Overhead bytes longer than the message it 10 | came from; this package will not obscure the length of the 11 | message. Keys, if they are not generated using the GenerateKey 12 | function, should be KeySize bytes long. The KeyIsSuitable function 13 | may be used to test a key is the proper length. 14 | 15 | This package also provides signed boxes: these digitally sign the 16 | message before sealing them, and the signature can be checked 17 | on opening. These must be opened with the OpenSigned function, 18 | and use ECDSA for signatures. 19 | 20 | The boxes used in this package are suitable for 20-year security. 21 | */ 22 | package box 23 | 24 | import ( 25 | "bytes" 26 | "crypto/ecdsa" 27 | "crypto/elliptic" 28 | "crypto/rand" 29 | "crypto/sha256" 30 | "github.com/kisom/aescrypt/secretbox" 31 | "math/big" 32 | ) 33 | 34 | type PublicKey []byte 35 | type PrivateKey []byte 36 | 37 | const VersionString = "2.0.0" 38 | 39 | const ( 40 | publicKeySize = 65 41 | privateKeySize = 32 42 | sigSize = 64 43 | ) 44 | 45 | const ( 46 | BoxUnsigned byte = 1 47 | BoxSigned byte = 2 48 | BoxShared byte = 11 49 | BoxSharedSigned byte = 12 50 | peerList = 21 51 | ) 52 | 53 | const ( 54 | SharedKeySize = 48 55 | ecdhSharedSize = 32 56 | ) 57 | 58 | // Overhead is the number of bytes of overhead when boxing a message. This will be greater 59 | // for locked and shared boxes. 60 | var Overhead = publicKeySize + secretbox.Overhead + 9 // 9: two four byte lengths and type 61 | 62 | // SignedOverhead is the number of bytes of overhead when signing and 63 | // boxing a message. 64 | var SignedOverhead = publicKeySize + secretbox.Overhead + sigSize 65 | 66 | // The default source for random data is the crypto/rand package's Reader. 67 | var PRNG = rand.Reader 68 | 69 | var curve = elliptic.P256() 70 | 71 | // ecdh performs the ECDH key agreement method to generate a shared key 72 | // between a pair of keys. 73 | func ecdh(key PrivateKey, peer PublicKey) ([]byte, bool) { 74 | x, y := elliptic.Unmarshal(curve, peer) 75 | if x == nil { 76 | return nil, false 77 | } else if !curve.IsOnCurve(x, y) { 78 | return nil, false 79 | } 80 | 81 | x, _ = curve.ScalarMult(x, y, key) 82 | if x == nil { 83 | return nil, false 84 | } 85 | xb := sha256.Sum256(zeroPad(x.Bytes(), SharedKeySize)) 86 | 87 | skey := xb[:16] 88 | mkey := xb[16:] 89 | h := sha256.New() 90 | h.Write(mkey) 91 | mkey = h.Sum(nil) 92 | 93 | return append(skey, mkey...), true 94 | } 95 | 96 | // SharedKey precomputes a key for encrypting with secretbox. 97 | func SharedKey(key PrivateKey, peer PublicKey) (secretbox.Key, bool) { 98 | return ecdh(key, peer) 99 | } 100 | 101 | // GenerateKey generates an appropriate private and public keypair for 102 | // use in box. 103 | func GenerateKey() (PrivateKey, PublicKey, bool) { 104 | key, x, y, err := elliptic.GenerateKey(curve, PRNG) 105 | if err != nil { 106 | return nil, nil, false 107 | } 108 | peer := elliptic.Marshal(curve, x, y) 109 | if peer == nil { 110 | } 111 | if len(key) != privateKeySize || len(peer) != publicKeySize { 112 | return nil, nil, false 113 | } 114 | return key, peer, true 115 | } 116 | 117 | func sealBox(message []byte, peer PublicKey, boxtype byte) *bw { 118 | if message == nil { 119 | return nil 120 | } else if !KeyIsSuitable(nil, peer) { 121 | return nil 122 | } 123 | 124 | eph_key, eph_peer, ok := GenerateKey() 125 | if !ok { 126 | return nil 127 | } 128 | defer zero(eph_key) 129 | 130 | skey, ok := ecdh(eph_key, peer) 131 | if !ok { 132 | return nil 133 | } 134 | defer zero(skey) 135 | 136 | packer := newbw([]byte{boxtype}) 137 | sbox, ok := secretbox.Seal(message, skey) 138 | if !ok { 139 | return nil 140 | } 141 | 142 | packer.Write(eph_peer) 143 | packer.Write(sbox) 144 | return packer 145 | } 146 | 147 | // Seal returns an authenticated and encrypted message, and a boolean 148 | // indicating whether the sealing operation was successful. If it returns 149 | // true, the message was successfully sealed. The box will be Overhead 150 | // bytes longer than the message. These boxes are not dependent on having 151 | // a private key. However, if a private key is passed in sigkey (with the 152 | // corresponding public key in sigpub), the box will be signed. 153 | func Seal(message []byte, peer PublicKey) (box []byte, ok bool) { 154 | packer := sealBox(message, peer, BoxUnsigned) 155 | if packer == nil { 156 | ok = false 157 | } else { 158 | box = packer.Bytes() 159 | if box == nil { 160 | ok = false 161 | } else { 162 | ok = true 163 | } 164 | } 165 | return 166 | } 167 | 168 | func openBox(box []byte, key PrivateKey) (btype byte, message []byte, ok bool) { 169 | if box == nil { 170 | return 0, nil, false 171 | } else if !KeyIsSuitable(key, nil) { 172 | return 0, nil, false 173 | } 174 | btype = box[0] 175 | unpacker := newbr(box[1:]) 176 | eph_pub := unpacker.Next() 177 | sbox := unpacker.Next() 178 | 179 | shared, ok := ecdh(key, eph_pub) 180 | if !ok { 181 | return 0, nil, false 182 | } 183 | 184 | message, ok = secretbox.Open(sbox, shared) 185 | if !ok { 186 | return 0, nil, false 187 | } 188 | 189 | return btype, message, true 190 | } 191 | 192 | // Open authenticates and decrypts a sealed message, also returning 193 | // whether the message was successfully opened. If this is false, the 194 | // message must be discarded. The returned message will be Overhead 195 | // bytes shorter than the box. 196 | func Open(box []byte, key PrivateKey) (message []byte, ok bool) { 197 | btype, message, ok := openBox(box, key) 198 | if !ok { 199 | return nil, false 200 | } else if message == nil { 201 | return nil, false 202 | } else if btype != BoxUnsigned { 203 | return nil, false 204 | } 205 | return message, true 206 | } 207 | 208 | func ecdsa_private(key PrivateKey, pub PublicKey) (skey *ecdsa.PrivateKey, ok bool) { 209 | x, y := elliptic.Unmarshal(curve, pub) 210 | if x == nil { 211 | return 212 | } 213 | 214 | skey = new(ecdsa.PrivateKey) 215 | skey.D = new(big.Int).SetBytes(key) 216 | skey.PublicKey.Curve = curve 217 | skey.X = x 218 | skey.Y = y 219 | ok = true 220 | return 221 | } 222 | 223 | func ecdsa_public(peer PublicKey) (pkey *ecdsa.PublicKey, ok bool) { 224 | x, y := elliptic.Unmarshal(curve, peer) 225 | if x == nil { 226 | return 227 | } 228 | pkey = &ecdsa.PublicKey{ 229 | Curve: curve, 230 | X: x, 231 | Y: y, 232 | } 233 | return pkey, true 234 | } 235 | 236 | // Sign is used to certify a message with the key pair passed in. It returns a 237 | // boolean indicating success; on success, the signature value returned will 238 | // contain the signature. 239 | func Sign(message []byte, key PrivateKey, pub PublicKey) (signature []byte, ok bool) { 240 | if message == nil { 241 | return nil, false 242 | } else if !KeyIsSuitable(key, pub) { 243 | return nil, false 244 | } 245 | h := sha256.New() 246 | h.Write(message) 247 | hash := h.Sum(nil) 248 | 249 | skey, ok := ecdsa_private(key, pub) 250 | if !ok { 251 | return 252 | } 253 | r, s, err := ecdsa.Sign(PRNG, skey, hash) 254 | if err != nil { 255 | ok = false 256 | } else { 257 | signature = marshalSignature(r, s) 258 | if signature == nil { 259 | ok = false 260 | } 261 | } 262 | return 263 | } 264 | 265 | // Verify returns true if the signature is a valid signature by the signer 266 | // for the message. If there is a failure (include failing to verify the 267 | // signature), Verify returns false. 268 | func Verify(message, signature []byte, signer PublicKey) bool { 269 | if message == nil || signature == nil { 270 | return false 271 | } else if !KeyIsSuitable(nil, signer) { 272 | return false 273 | } 274 | r, s := unmarshalSignature(signature) 275 | if r == nil || s == nil { 276 | return false 277 | } 278 | h := sha256.New() 279 | h.Write(message) 280 | 281 | pub, ok := ecdsa_public(signer) 282 | if !ok { 283 | return false 284 | } 285 | return ecdsa.Verify(pub, h.Sum(nil), r, s) 286 | } 287 | 288 | // SignAndSeal adds a digital signature to the message before sealing it. 289 | func SignAndSeal(message []byte, key PrivateKey, public PublicKey, peer PublicKey) (box []byte, ok bool) { 290 | sig, ok := Sign(message, key, public) 291 | if !ok || sig == nil { 292 | return nil, false 293 | } 294 | mpack := newbw(nil) 295 | mpack.Write(message) 296 | mpack.Write(sig) 297 | signedMessage := mpack.Bytes() 298 | if signedMessage == nil { 299 | return nil, false 300 | } 301 | defer zero(signedMessage) 302 | packer := sealBox(signedMessage, peer, BoxSigned) 303 | if packer == nil { 304 | return nil, false 305 | } 306 | box = packer.Bytes() 307 | if box == nil { 308 | return nil, false 309 | } 310 | return box, true 311 | } 312 | 313 | // OpenAndVerify opens a signed box, and verifies the signature. If the box 314 | // couldn't be opened or the signature is invalid, OpenAndVerify returns false, 315 | // and the message value must be discarded. 316 | func OpenAndVerify(box []byte, key PrivateKey, peer PublicKey) (message []byte, ok bool) { 317 | btype, smessage, ok := openBox(box, key) 318 | if !ok || smessage == nil { 319 | return nil, false 320 | } else if btype != BoxSigned { 321 | return nil, false 322 | } 323 | mpack := newbr(smessage) 324 | message = mpack.Next() 325 | if message == nil { 326 | return nil, false 327 | } 328 | sig := mpack.Next() 329 | if sig == nil { 330 | return nil, false 331 | } 332 | 333 | if !Verify(message, sig, peer) { 334 | return nil, false 335 | } 336 | return message, true 337 | } 338 | 339 | // BoxIsSigned returns true if the box is a signed box, and false otherwise. 340 | func BoxIsSigned(box []byte) bool { 341 | if box == nil { 342 | return false 343 | } else if box[0] == BoxSigned { 344 | return true 345 | } else if box[0] == BoxSharedSigned { 346 | return true 347 | } else { 348 | return false 349 | } 350 | } 351 | 352 | // IsKeySuitable takes a private and/or public key, and returns true if 353 | // all keys passed in are valid. If no key is passed in, or any key passed 354 | // in is invalid, it will return false. 355 | func KeyIsSuitable(key PrivateKey, pub PublicKey) bool { 356 | if key == nil && pub == nil { 357 | return false 358 | } else if key != nil && len(key) != privateKeySize { 359 | return false 360 | } else if pub != nil && len(pub) != publicKeySize { 361 | return false 362 | } 363 | return true 364 | } 365 | 366 | // SignKey takes the key pair specified in priv, pub and uses that to 367 | // sign the peer key. It returns a signature and true on success; 368 | // if ok is false, the signature should be discarded as signing failed. 369 | func SignKey(priv PrivateKey, pub, peer PublicKey) (sig []byte, ok bool) { 370 | key, ok := ecdsa_private(priv, pub) 371 | if !ok { 372 | return nil, false 373 | } 374 | 375 | h := sha256.New() 376 | h.Write(peer) 377 | m := h.Sum(nil) 378 | r, s, err := ecdsa.Sign(PRNG, key, m) 379 | if err != nil { 380 | return nil, false 381 | } 382 | sig = marshalSignature(r, s) 383 | if sig == nil { 384 | return nil, false 385 | } 386 | return sig, true 387 | } 388 | 389 | // VerifySign checks the signature on the peer key with the sigpub 390 | // key. It returns true if the signature is valid, or false if the 391 | // signature is invalid or an error occurred. 392 | func VerifySignedKey(pub, sigpub PublicKey, sig []byte) bool { 393 | ecpub, ok := ecdsa_public(sigpub) 394 | if !ok { 395 | return false 396 | } 397 | 398 | r, s := unmarshalSignature(sig) 399 | if r == nil || s == nil { 400 | return false 401 | } 402 | 403 | h := sha256.New() 404 | h.Write(pub) 405 | m := h.Sum(nil) 406 | return ecdsa.Verify(ecpub, m, r, s) 407 | } 408 | 409 | func boxForPeer(e_priv PrivateKey, peer PublicKey, key secretbox.Key) ([]byte, bool) { 410 | shared, ok := ecdh(e_priv, peer) 411 | if !ok { 412 | return nil, false 413 | } 414 | defer zero(shared) 415 | return secretbox.Seal(key, shared) 416 | 417 | } 418 | 419 | func buildSharedBox(message []byte, peers []PublicKey, btype byte) []byte { 420 | if message == nil { 421 | return nil 422 | } 423 | 424 | for _, peer := range peers { 425 | if peer == nil { 426 | return nil 427 | } else if !KeyIsSuitable(nil, peer) { 428 | return nil 429 | } 430 | } 431 | 432 | e_priv, e_pub, ok := GenerateKey() 433 | if !ok { 434 | return nil 435 | } 436 | 437 | shared, ok := secretbox.GenerateKey() 438 | if !ok { 439 | return nil 440 | } 441 | defer zero(shared) 442 | 443 | packPeers := newbw([]byte{peerList}) 444 | packPeers.WriteUint32(uint32(len(peers))) 445 | for _, peer := range peers { 446 | packPeers.Write(peer) 447 | pbox, ok := boxForPeer(e_priv, peer, shared) 448 | if !ok { 449 | return nil 450 | } 451 | packPeers.Write(pbox) 452 | } 453 | plist := packPeers.Bytes() 454 | if plist == nil { 455 | return nil 456 | } 457 | 458 | packer := newbw([]byte{btype}) 459 | packer.Write(e_pub) 460 | packer.Write(plist) 461 | sbox, ok := secretbox.Seal(message, shared) 462 | if !ok { 463 | return nil 464 | } 465 | packer.Write(sbox) 466 | return packer.Bytes() 467 | } 468 | 469 | // SealShared returns an authenticated and encrypted message shared 470 | // between multiple peers, and a boolean indicating whether the sealing 471 | // operation was successful. If it returns true, the message was 472 | // successfully sealed. These boxes are not dependent on having a private 473 | // key. 474 | func SealShared(message []byte, peers []PublicKey) (box []byte, ok bool) { 475 | box = buildSharedBox(message, peers, BoxShared) 476 | if box == nil { 477 | ok = false 478 | } else { 479 | ok = true 480 | } 481 | return box, ok 482 | } 483 | 484 | // SignAndSealShared adds a digital signature to the shared message before 485 | // sealing it. 486 | func SignAndSealShared(message []byte, peers []PublicKey, sigkey PrivateKey, sigpub PublicKey) (box []byte, ok bool) { 487 | sig, ok := Sign(message, sigkey, sigpub) 488 | if !ok { 489 | return nil, false 490 | } 491 | mpack := newbw(nil) 492 | mpack.Write(message) 493 | mpack.Write(sig) 494 | signedMessage := mpack.Bytes() 495 | if signedMessage == nil { 496 | return nil, false 497 | } 498 | defer zero(signedMessage) 499 | 500 | box = buildSharedBox(signedMessage, peers, BoxSharedSigned) 501 | if box == nil { 502 | ok = false 503 | } else { 504 | ok = true 505 | } 506 | return box, ok 507 | } 508 | 509 | func unpackSharedBox(box []byte, key PrivateKey, public PublicKey) (btype byte, message []byte, ok bool) { 510 | if box == nil { 511 | return 0, nil, false 512 | } else if !KeyIsSuitable(key, public) { 513 | return 0, nil, false 514 | } 515 | btype = box[0] 516 | 517 | unpacker := newbr(box[1:]) 518 | e_pub := unpacker.Next() 519 | if e_pub == nil { 520 | return 0, nil, false 521 | } 522 | 523 | packedPeers := unpacker.Next() 524 | if packedPeers == nil { 525 | return 0, nil, false 526 | } else if packedPeers[0] != peerList { 527 | return 0, nil, false 528 | } 529 | peerUnpack := newbr(packedPeers[1:]) 530 | peerCount, ok := peerUnpack.NextU32() 531 | if !ok { 532 | return 0, nil, false 533 | } 534 | 535 | var shared []byte = nil 536 | defer zero(shared) 537 | 538 | for i := uint32(0); i < peerCount; i++ { 539 | peer := peerUnpack.Next() 540 | if peer == nil { 541 | return 0, nil, false 542 | } 543 | sbox := peerUnpack.Next() 544 | if sbox == nil { 545 | return 0, nil, false 546 | } else if !bytes.Equal(peer, public) { 547 | continue 548 | } 549 | skey, ok := ecdh(key, e_pub) 550 | if !ok { 551 | return 0, nil, false 552 | } 553 | shared, ok = secretbox.Open(sbox, skey) 554 | if !ok { 555 | return 0, nil, false 556 | } 557 | break 558 | } 559 | if shared == nil { 560 | return 0, nil, false 561 | } 562 | sbox := unpacker.Next() 563 | if sbox == nil { 564 | return 0, nil, false 565 | } 566 | message, ok = secretbox.Open(sbox, shared) 567 | return btype, message, ok 568 | } 569 | 570 | // OpenShared authenticates and decrypts a sealed shared message, also 571 | // returning whether the message was successfully opened. If this is 572 | // false, the message must be discarded. 573 | func OpenShared(box []byte, key PrivateKey, public PublicKey) (message []byte, ok bool) { 574 | btype, message, ok := unpackSharedBox(box, key, public) 575 | if !ok { 576 | return nil, false 577 | } else if message == nil { 578 | return nil, false 579 | } else if btype != BoxShared { 580 | return nil, false 581 | } 582 | return message, true 583 | } 584 | 585 | // OpenSharedAndVerify opens a signed shared box, and verifies the 586 | // signature. If the box couldn't be opened or the signature is invalid, 587 | // OpenSharedAndVerify returns false, and the message value must be 588 | // discarded. 589 | func OpenSharedAndVerify(box []byte, key PrivateKey, public PublicKey, signer PublicKey) (message []byte, ok bool) { 590 | btype, smessage, ok := unpackSharedBox(box, key, public) 591 | if !ok { 592 | return nil, false 593 | } else if smessage == nil { 594 | return nil, false 595 | } else if btype != BoxSharedSigned { 596 | return nil, false 597 | } 598 | 599 | mpack := newbr(smessage) 600 | message = mpack.Next() 601 | if message == nil { 602 | return nil, false 603 | } 604 | sig := mpack.Next() 605 | if sig == nil { 606 | return nil, false 607 | } 608 | 609 | if !Verify(message, sig, signer) { 610 | return nil, false 611 | } 612 | return message, true 613 | } 614 | -------------------------------------------------------------------------------- /stoutbox/stoutbox.go: -------------------------------------------------------------------------------- 1 | /* 2 | stoutbox is used to authenticate and secure messages using 3 | public-key cryptography. It provides an interface similar to 4 | NaCL, but uses ECIES using ephemeral ECDH for shared keys, and 5 | secret box for securing messages. 6 | 7 | Messages should be secured using the Seal function, and recovered 8 | using the Open function. A box (or authenticated and encrypted 9 | message) will be Overhead bytes longer than the message it came 10 | from; this package will not obscure the length of the message. 11 | Keys, if they are not generated using the GenerateKey function, 12 | should be KeySize bytes long. The KeyIsSuitable function may be 13 | used to test a key is the proper length. 14 | 15 | This package also provides signed boxes: these digitally sign the 16 | message before sealing them, and the signature can be checked on 17 | opening. These must be opened with the OpenSigned function, and 18 | use ECDSA for signatures. 19 | 20 | The boxes used in this package are suitable for 50-year security. 21 | */ 22 | package stoutbox 23 | 24 | import ( 25 | "bytes" 26 | "crypto/ecdsa" 27 | "crypto/elliptic" 28 | "crypto/rand" 29 | "crypto/sha512" 30 | "github.com/kisom/aescrypt/strongbox" 31 | "math/big" 32 | ) 33 | 34 | type PublicKey []byte 35 | type PrivateKey []byte 36 | 37 | const VersionString = "2.0.0" 38 | const ( 39 | publicKeySize = 133 40 | privateKeySize = 66 41 | sigSize = 140 42 | ) 43 | 44 | const ( 45 | BoxUnsigned byte = 1 46 | BoxSigned byte = 2 47 | BoxShared byte = 11 48 | BoxSharedSigned byte = 12 49 | peerList = 21 50 | ) 51 | 52 | const ( 53 | SharedKeySize = 80 54 | ecdhSharedSize = 80 55 | ) 56 | 57 | // Overhead is the number of bytes of overhead when boxing a message. This will be greater 58 | // for locked and shared boxes. 59 | var Overhead = publicKeySize + strongbox.Overhead + 9 // 9: two four byte lengths and type 60 | 61 | // SignedOverhead is the number of bytes of overhead when signing and 62 | // boxing a message. 63 | var SignedOverhead = publicKeySize + strongbox.Overhead + sigSize 64 | 65 | // The default source for random data is the crypto/rand package's Reader. 66 | var PRNG = rand.Reader 67 | 68 | var curve = elliptic.P521() 69 | 70 | // ecdh performs the ECDH key agreement method to generate a shared key 71 | // between a pair of keys. 72 | func ecdh(key PrivateKey, peer PublicKey) ([]byte, bool) { 73 | x, y := elliptic.Unmarshal(curve, peer) 74 | if x == nil { 75 | return nil, false 76 | } else if !curve.IsOnCurve(x, y) { 77 | return nil, false 78 | } 79 | 80 | x, _ = curve.ScalarMult(x, y, key) 81 | if x == nil { 82 | return nil, false 83 | } 84 | xb := sha512.Sum512(x.Bytes()) 85 | 86 | skey := xb[:32] 87 | mkey := xb[32:] 88 | h := sha512.New384() 89 | h.Write(mkey) 90 | mkey = h.Sum(nil) 91 | 92 | return append(skey, mkey...), true 93 | } 94 | 95 | // SharedKey precomputes a key for encrypting with strongbox. 96 | func SharedKey(key PrivateKey, peer PublicKey) (strongbox.Key, bool) { 97 | return ecdh(key, peer) 98 | } 99 | 100 | // GenerateKey generates an appropriate private and public keypair for 101 | // use in box. 102 | func GenerateKey() (PrivateKey, PublicKey, bool) { 103 | key, x, y, err := elliptic.GenerateKey(curve, PRNG) 104 | if err != nil { 105 | return nil, nil, false 106 | } 107 | peer := elliptic.Marshal(curve, x, y) 108 | if peer == nil { 109 | } 110 | if len(key) != privateKeySize || len(peer) != publicKeySize { 111 | return nil, nil, false 112 | } 113 | return key, peer, true 114 | } 115 | 116 | func sealBox(message []byte, peer PublicKey, boxtype byte) *bw { 117 | if message == nil { 118 | return nil 119 | } else if !KeyIsSuitable(nil, peer) { 120 | return nil 121 | } 122 | 123 | eph_key, eph_peer, ok := GenerateKey() 124 | if !ok { 125 | return nil 126 | } 127 | defer zero(eph_key) 128 | 129 | skey, ok := ecdh(eph_key, peer) 130 | if !ok { 131 | return nil 132 | } 133 | defer zero(skey) 134 | 135 | packer := newbw([]byte{boxtype}) 136 | sbox, ok := strongbox.Seal(message, skey) 137 | if !ok { 138 | return nil 139 | } 140 | 141 | packer.Write(eph_peer) 142 | packer.Write(sbox) 143 | return packer 144 | } 145 | 146 | // Seal returns an authenticated and encrypted message, and a boolean 147 | // indicating whether the sealing operation was successful. If it returns 148 | // true, the message was successfully sealed. The box will be Overhead 149 | // bytes longer than the message. These boxes are not dependent on having 150 | // a private key. However, if a private key is passed in sigkey (with the 151 | // corresponding public key in sigpub), the box will be signed. 152 | func Seal(message []byte, peer PublicKey) (box []byte, ok bool) { 153 | packer := sealBox(message, peer, BoxUnsigned) 154 | if packer == nil { 155 | ok = false 156 | } else { 157 | box = packer.Bytes() 158 | if box == nil { 159 | ok = false 160 | } else { 161 | ok = true 162 | } 163 | } 164 | return 165 | } 166 | 167 | func openBox(box []byte, key PrivateKey) (btype byte, message []byte, ok bool) { 168 | if box == nil { 169 | return 0, nil, false 170 | } else if !KeyIsSuitable(key, nil) { 171 | return 0, nil, false 172 | } 173 | btype = box[0] 174 | unpacker := newbr(box[1:]) 175 | eph_pub := unpacker.Next() 176 | sbox := unpacker.Next() 177 | 178 | shared, ok := ecdh(key, eph_pub) 179 | if !ok { 180 | return 0, nil, false 181 | } 182 | 183 | message, ok = strongbox.Open(sbox, shared) 184 | if !ok { 185 | return 0, nil, false 186 | } 187 | 188 | return btype, message, true 189 | } 190 | 191 | // Open authenticates and decrypts a sealed message, also returning 192 | // whether the message was successfully opened. If this is false, the 193 | // message must be discarded. The returned message will be Overhead 194 | // bytes shorter than the box. 195 | func Open(box []byte, key PrivateKey) (message []byte, ok bool) { 196 | btype, message, ok := openBox(box, key) 197 | if !ok { 198 | return nil, false 199 | } else if message == nil { 200 | return nil, false 201 | } else if btype != BoxUnsigned { 202 | return nil, false 203 | } 204 | return message, true 205 | } 206 | 207 | func ecdsa_private(key PrivateKey, pub PublicKey) (skey *ecdsa.PrivateKey, ok bool) { 208 | x, y := elliptic.Unmarshal(curve, pub) 209 | if x == nil { 210 | return 211 | } 212 | 213 | skey = new(ecdsa.PrivateKey) 214 | skey.D = new(big.Int).SetBytes(key) 215 | skey.PublicKey.Curve = curve 216 | skey.X = x 217 | skey.Y = y 218 | ok = true 219 | return 220 | } 221 | 222 | func ecdsa_public(peer PublicKey) (pkey *ecdsa.PublicKey, ok bool) { 223 | x, y := elliptic.Unmarshal(curve, peer) 224 | if x == nil { 225 | return 226 | } 227 | pkey = &ecdsa.PublicKey{ 228 | Curve: curve, 229 | X: x, 230 | Y: y, 231 | } 232 | return pkey, true 233 | } 234 | 235 | // Sign is used to certify a message with the key pair passed in. It returns a 236 | // boolean indicating success; on success, the signature value returned will 237 | // contain the signature. 238 | func Sign(message []byte, key PrivateKey, pub PublicKey) (signature []byte, ok bool) { 239 | if message == nil { 240 | return nil, false 241 | } else if !KeyIsSuitable(key, pub) { 242 | return nil, false 243 | } 244 | h := sha512.New384() 245 | h.Write(message) 246 | hash := h.Sum(nil) 247 | 248 | skey, ok := ecdsa_private(key, pub) 249 | if !ok { 250 | return 251 | } 252 | r, s, err := ecdsa.Sign(PRNG, skey, hash) 253 | if err != nil { 254 | ok = false 255 | } else { 256 | signature = marshalSignature(r, s) 257 | if signature == nil { 258 | ok = false 259 | } 260 | } 261 | return 262 | } 263 | 264 | // Verify returns true if the signature is a valid signature by the signer 265 | // for the message. If there is a failure (include failing to verify the 266 | // signature), Verify returns false. 267 | func Verify(message, signature []byte, signer PublicKey) bool { 268 | if message == nil || signature == nil { 269 | return false 270 | } else if !KeyIsSuitable(nil, signer) { 271 | return false 272 | } 273 | r, s := unmarshalSignature(signature) 274 | if r == nil || s == nil { 275 | return false 276 | } 277 | h := sha512.New384() 278 | h.Write(message) 279 | 280 | pub, ok := ecdsa_public(signer) 281 | if !ok { 282 | return false 283 | } 284 | return ecdsa.Verify(pub, h.Sum(nil), r, s) 285 | } 286 | 287 | // SignAndSeal adds a digital signature to the message before sealing it. 288 | func SignAndSeal(message []byte, key PrivateKey, public PublicKey, peer PublicKey) (box []byte, ok bool) { 289 | sig, ok := Sign(message, key, public) 290 | if !ok || sig == nil { 291 | return nil, false 292 | } 293 | mpack := newbw(nil) 294 | mpack.Write(message) 295 | mpack.Write(sig) 296 | signedMessage := mpack.Bytes() 297 | if signedMessage == nil { 298 | return nil, false 299 | } 300 | defer zero(signedMessage) 301 | packer := sealBox(signedMessage, peer, BoxSigned) 302 | if packer == nil { 303 | return nil, false 304 | } 305 | box = packer.Bytes() 306 | if box == nil { 307 | return nil, false 308 | } 309 | return box, true 310 | } 311 | 312 | // OpenAndVerify opens a signed box, and verifies the signature. If the box 313 | // couldn't be opened or the signature is invalid, OpenAndVerify returns false, 314 | // and the message value must be discarded. 315 | func OpenAndVerify(box []byte, key PrivateKey, peer PublicKey) (message []byte, ok bool) { 316 | btype, smessage, ok := openBox(box, key) 317 | if !ok || smessage == nil { 318 | return nil, false 319 | } else if btype != BoxSigned { 320 | return nil, false 321 | } 322 | mpack := newbr(smessage) 323 | message = mpack.Next() 324 | if message == nil { 325 | return nil, false 326 | } 327 | sig := mpack.Next() 328 | if sig == nil { 329 | return nil, false 330 | } 331 | 332 | if !Verify(message, sig, peer) { 333 | return nil, false 334 | } 335 | return message, true 336 | } 337 | 338 | // BoxIsSigned returns true if the box is a signed box, and false otherwise. 339 | func BoxIsSigned(box []byte) bool { 340 | if box == nil { 341 | return false 342 | } else if box[0] == BoxSigned { 343 | return true 344 | } else if box[0] == BoxSharedSigned { 345 | return true 346 | } else { 347 | return false 348 | } 349 | } 350 | 351 | // IsKeySuitable takes a private and/or public key, and returns true if 352 | // all keys passed in are valid. If no key is passed in, or any key passed 353 | // in is invalid, it will return false. 354 | func KeyIsSuitable(key PrivateKey, pub PublicKey) bool { 355 | if key == nil && pub == nil { 356 | return false 357 | } else if key != nil && len(key) != privateKeySize { 358 | return false 359 | } else if pub != nil && len(pub) != publicKeySize { 360 | return false 361 | } 362 | return true 363 | } 364 | 365 | // SignKey takes the key pair specified in priv, pub and uses that to 366 | // sign the peer key. It returns a signature and true on success; 367 | // if ok is false, the signature should be discarded as signing failed. 368 | func SignKey(priv PrivateKey, pub, peer PublicKey) (sig []byte, ok bool) { 369 | key, ok := ecdsa_private(priv, pub) 370 | if !ok { 371 | return nil, false 372 | } 373 | 374 | h := sha512.New384() 375 | h.Write(peer) 376 | m := h.Sum(nil) 377 | r, s, err := ecdsa.Sign(PRNG, key, m) 378 | if err != nil { 379 | return nil, false 380 | } 381 | sig = marshalSignature(r, s) 382 | if sig == nil { 383 | return nil, false 384 | } 385 | return sig, true 386 | } 387 | 388 | // VerifySign checks the signature on the peer key with the sigpub 389 | // key. It returns true if the signature is valid, or false if the 390 | // signature is invalid or an error occurred. 391 | func VerifySignedKey(pub, sigpub PublicKey, sig []byte) bool { 392 | ecpub, ok := ecdsa_public(sigpub) 393 | if !ok { 394 | return false 395 | } 396 | 397 | r, s := unmarshalSignature(sig) 398 | if r == nil || s == nil { 399 | return false 400 | } 401 | 402 | h := sha512.New384() 403 | h.Write(pub) 404 | m := h.Sum(nil) 405 | return ecdsa.Verify(ecpub, m, r, s) 406 | } 407 | 408 | func boxForPeer(e_priv PrivateKey, peer PublicKey, key strongbox.Key) ([]byte, bool) { 409 | shared, ok := ecdh(e_priv, peer) 410 | if !ok { 411 | return nil, false 412 | } 413 | defer zero(shared) 414 | return strongbox.Seal(key, shared) 415 | 416 | } 417 | 418 | func buildSharedBox(message []byte, peers []PublicKey, btype byte) []byte { 419 | if message == nil { 420 | return nil 421 | } 422 | 423 | for _, peer := range peers { 424 | if peer == nil { 425 | return nil 426 | } else if !KeyIsSuitable(nil, peer) { 427 | return nil 428 | } 429 | } 430 | 431 | e_priv, e_pub, ok := GenerateKey() 432 | if !ok { 433 | return nil 434 | } 435 | 436 | shared, ok := strongbox.GenerateKey() 437 | if !ok { 438 | return nil 439 | } 440 | defer zero(shared) 441 | 442 | packPeers := newbw([]byte{peerList}) 443 | packPeers.WriteUint32(uint32(len(peers))) 444 | for _, peer := range peers { 445 | packPeers.Write(peer) 446 | pbox, ok := boxForPeer(e_priv, peer, shared) 447 | if !ok { 448 | return nil 449 | } 450 | packPeers.Write(pbox) 451 | } 452 | plist := packPeers.Bytes() 453 | if plist == nil { 454 | return nil 455 | } 456 | 457 | packer := newbw([]byte{btype}) 458 | packer.Write(e_pub) 459 | packer.Write(plist) 460 | sbox, ok := strongbox.Seal(message, shared) 461 | if !ok { 462 | return nil 463 | } 464 | packer.Write(sbox) 465 | return packer.Bytes() 466 | } 467 | 468 | // SealShared returns an authenticated and encrypted message shared 469 | // between multiple peers, and a boolean indicating whether the sealing 470 | // operation was successful. If it returns true, the message was 471 | // successfully sealed. These boxes are not dependent on having a private 472 | // key. 473 | func SealShared(message []byte, peers []PublicKey) (box []byte, ok bool) { 474 | box = buildSharedBox(message, peers, BoxShared) 475 | if box == nil { 476 | ok = false 477 | } else { 478 | ok = true 479 | } 480 | return box, ok 481 | } 482 | 483 | // SignAndSeal adds a digital signature to the shared message before 484 | // sealing it. 485 | func SignAndSealShared(message []byte, peers []PublicKey, sigkey PrivateKey, sigpub PublicKey) (box []byte, ok bool) { 486 | sig, ok := Sign(message, sigkey, sigpub) 487 | if !ok { 488 | return nil, false 489 | } 490 | mpack := newbw(nil) 491 | mpack.Write(message) 492 | mpack.Write(sig) 493 | signedMessage := mpack.Bytes() 494 | if signedMessage == nil { 495 | return nil, false 496 | } 497 | defer zero(signedMessage) 498 | 499 | box = buildSharedBox(signedMessage, peers, BoxSharedSigned) 500 | if box == nil { 501 | ok = false 502 | } else { 503 | ok = true 504 | } 505 | return box, ok 506 | } 507 | 508 | func unpackSharedBox(box []byte, key PrivateKey, public PublicKey) (btype byte, message []byte, ok bool) { 509 | if box == nil { 510 | return 0, nil, false 511 | } else if !KeyIsSuitable(key, public) { 512 | return 0, nil, false 513 | } 514 | btype = box[0] 515 | 516 | unpacker := newbr(box[1:]) 517 | e_pub := unpacker.Next() 518 | if e_pub == nil { 519 | return 0, nil, false 520 | } 521 | 522 | packedPeers := unpacker.Next() 523 | if packedPeers == nil { 524 | return 0, nil, false 525 | } else if packedPeers[0] != peerList { 526 | return 0, nil, false 527 | } 528 | peerUnpack := newbr(packedPeers[1:]) 529 | peerCount, ok := peerUnpack.NextU32() 530 | if !ok { 531 | return 0, nil, false 532 | } 533 | 534 | var shared []byte = nil 535 | defer zero(shared) 536 | 537 | for i := uint32(0); i < peerCount; i++ { 538 | peer := peerUnpack.Next() 539 | if peer == nil { 540 | return 0, nil, false 541 | } 542 | sbox := peerUnpack.Next() 543 | if sbox == nil { 544 | return 0, nil, false 545 | } else if !bytes.Equal(peer, public) { 546 | continue 547 | } 548 | skey, ok := ecdh(key, e_pub) 549 | if !ok { 550 | return 0, nil, false 551 | } 552 | shared, ok = strongbox.Open(sbox, skey) 553 | if !ok { 554 | return 0, nil, false 555 | } 556 | break 557 | } 558 | if shared == nil { 559 | return 0, nil, false 560 | } 561 | sbox := unpacker.Next() 562 | if sbox == nil { 563 | return 0, nil, false 564 | } 565 | message, ok = strongbox.Open(sbox, shared) 566 | if !ok { 567 | } 568 | return btype, message, ok 569 | } 570 | 571 | // OpenShared authenticates and decrypts a sealed shared message, also 572 | // returning whether the message was successfully opened. If this is 573 | // false, the message must be discarded. 574 | func OpenShared(box []byte, key PrivateKey, public PublicKey) (message []byte, ok bool) { 575 | btype, message, ok := unpackSharedBox(box, key, public) 576 | if !ok { 577 | return nil, false 578 | } else if message == nil { 579 | return nil, false 580 | } else if btype != BoxShared { 581 | return nil, false 582 | } 583 | return message, true 584 | } 585 | 586 | // OpenSharedAndVerify opens a signed shared box, and verifies the 587 | // signature. If the box couldn't be opened or the signature is invalid, 588 | // OpenSharedAndVerify returns false, and the message value must be 589 | // discarded. 590 | func OpenSharedAndVerify(box []byte, key PrivateKey, public PublicKey, signer PublicKey) (message []byte, ok bool) { 591 | btype, smessage, ok := unpackSharedBox(box, key, public) 592 | if !ok { 593 | return nil, false 594 | } else if smessage == nil { 595 | return nil, false 596 | } else if btype != BoxSharedSigned { 597 | return nil, false 598 | } 599 | 600 | mpack := newbr(smessage) 601 | message = mpack.Next() 602 | if message == nil { 603 | return nil, false 604 | } 605 | sig := mpack.Next() 606 | if sig == nil { 607 | return nil, false 608 | } 609 | 610 | if !Verify(message, sig, signer) { 611 | return nil, false 612 | } 613 | return message, true 614 | } 615 | -------------------------------------------------------------------------------- /box/box_test.go: -------------------------------------------------------------------------------- 1 | package box 2 | 3 | import "bytes" 4 | import "crypto/rand" 5 | import "fmt" 6 | import "io/ioutil" 7 | import "math/big" 8 | import "testing" 9 | 10 | var testMessages = []string{ 11 | "Hello, world.", 12 | "Yes... yes. This is a fertile land, and we will thrive. We will rule over all this land, and we will call it... This Land.", 13 | "Ah! Curse your sudden but inevitable betrayal!", 14 | "And I'm thinkin' you weren't burdened with an overabundance of schooling. So why don't we just ignore each other until we go away?", 15 | "Sir, I think you have a problem with your brain being missing.", 16 | "It's the way of life in my findings that journeys end when and where they want to; and that's where you make your home.", 17 | "I get confused. I remember everything. I remember too much. And... some of it's made up, and... some of it can't be quantified, and... there's secrets... and...", 18 | "Yeah, we're pretty much just giving each other significant glances and laughing incessantly.", 19 | "Jayne, go play with your rainstick.", 20 | } 21 | 22 | var ( 23 | testBoxes = make([]string, len(testMessages)) 24 | testBoxFile []byte 25 | testGoodKey = PrivateKey{ 26 | 0x17, 0xa0, 0x63, 0x3d, 0xfa, 0xef, 0x1b, 0x86, 27 | 0x4d, 0xc1, 0x9c, 0x09, 0xe0, 0xeb, 0xd8, 0xfa, 28 | 0x3d, 0x72, 0x68, 0xc0, 0x49, 0xf2, 0x08, 0x05, 29 | 0x75, 0xe2, 0x57, 0x86, 0xec, 0x2a, 0xf6, 0x93, 30 | } 31 | testGoodPub = PublicKey{ 32 | 0x04, 0x47, 0x10, 0x32, 0x8b, 0x3d, 0xb7, 0x7c, 33 | 0x81, 0xb1, 0x33, 0x6e, 0xed, 0x21, 0x40, 0x75, 34 | 0x1b, 0x44, 0x54, 0x9f, 0xdc, 0x19, 0xf0, 0x7c, 35 | 0xdc, 0x46, 0x7a, 0xb1, 0x19, 0x07, 0x13, 0x78, 36 | 0x20, 0x3e, 0x1e, 0x1e, 0xeb, 0xbd, 0xc8, 0x31, 37 | 0x74, 0x1a, 0x23, 0xfc, 0xcc, 0x08, 0x6f, 0x5f, 38 | 0xba, 0xb7, 0xa1, 0x24, 0x22, 0xa3, 0xc1, 0xd1, 39 | 0x14, 0x2a, 0x55, 0x69, 0xf8, 0x0f, 0x25, 0xed, 40 | 0x94, 41 | } 42 | testPeerKey = PrivateKey{ 43 | 0xcc, 0xbf, 0x0c, 0x08, 0x72, 0x2a, 0xc7, 0xf0, 44 | 0xd2, 0xef, 0x6c, 0x06, 0xef, 0x7c, 0x7d, 0x6a, 45 | 0xba, 0x0b, 0xb4, 0xe0, 0xc6, 0xd2, 0x88, 0x19, 46 | 0xa4, 0x6d, 0x32, 0xa1, 0x3a, 0x10, 0x0c, 0xe4, 47 | } 48 | testPeerPub = PublicKey{ 49 | 0x04, 0x1e, 0x1e, 0x75, 0xe7, 0xbc, 0x75, 0x9b, 50 | 0xa8, 0x4c, 0x3c, 0x09, 0x15, 0x9d, 0x09, 0x55, 51 | 0x35, 0x7e, 0x7f, 0x45, 0xa9, 0xef, 0x55, 0xd2, 52 | 0x65, 0xd1, 0x84, 0xba, 0x9c, 0x53, 0x03, 0x25, 53 | 0x5a, 0xa9, 0xe0, 0x07, 0xfb, 0x3f, 0xb3, 0x5f, 54 | 0x6d, 0xf7, 0xcc, 0xf5, 0x0f, 0x8a, 0x9e, 0xf8, 55 | 0x5e, 0x5f, 0x30, 0xd2, 0xfd, 0x3d, 0xf6, 0x0d, 56 | 0x7d, 0x01, 0x82, 0xdc, 0xba, 0x51, 0x19, 0x12, 57 | 0x13, 58 | } 59 | testBadKey = PrivateKey{ 60 | 0xa8, 0xa7, 0x23, 0xa7, 0x18, 0x47, 0x5a, 0x4a, 61 | 0x52, 0xb9, 0xf8, 0x77, 0x10, 0x66, 0x9e, 0xe7, 62 | 0x0c, 0x28, 0x03, 0xfb, 0x90, 0x73, 0x6e, 0x9d, 63 | 0xcf, 0x40, 0xdf, 0xba, 0x80, 0x22, 0x79, 0xcf, 64 | } 65 | testBadPub = PublicKey{ 66 | 0x04, 0x86, 0xa4, 0x50, 0x1a, 0x00, 0x1b, 0x00, 67 | 0x11, 0xac, 0x0b, 0xed, 0x9f, 0x5b, 0x7d, 0xd6, 68 | 0xa7, 0x82, 0x88, 0xb8, 0x42, 0xd9, 0x8f, 0x03, 69 | 0x0a, 0x4a, 0xab, 0x90, 0x0c, 0x93, 0x05, 0xce, 70 | 0xf2, 0x9c, 0x62, 0x0d, 0xd5, 0x8e, 0xff, 0x49, 71 | 0x9a, 0x7a, 0xea, 0xbf, 0x49, 0xaf, 0x6f, 0xa0, 72 | 0x02, 0x67, 0x69, 0xe8, 0x38, 0x30, 0x11, 0xde, 73 | 0xa4, 0x0e, 0xf9, 0xaa, 0x3b, 0xc4, 0x8a, 0x5c, 74 | 0x9d, 75 | } 76 | ) 77 | 78 | func randInt(max int64) int64 { 79 | maxBig := big.NewInt(max) 80 | n, err := rand.Int(PRNG, maxBig) 81 | if err != nil { 82 | return -1 83 | } 84 | return n.Int64() 85 | } 86 | 87 | func mutate(in []byte) (out []byte) { 88 | out = make([]byte, len(in)) 89 | copy(out, in) 90 | 91 | iterations := (randInt(int64(len(out))) / 2) + 1 92 | if iterations == -1 { 93 | panic("mutate failed") 94 | } 95 | for i := 0; i < int(iterations); i++ { 96 | mByte := randInt(int64(len(out))) 97 | mBit := randInt(7) 98 | if mBit == -1 || mByte == -1 { 99 | panic("mutate failed") 100 | } 101 | out[mByte] ^= (1 << uint(mBit)) 102 | } 103 | if bytes.Equal(out, in) { 104 | panic("mutate failed") 105 | } 106 | return out 107 | } 108 | 109 | /* 110 | func TestKeyGeneration(t *testing.T) { 111 | var ok bool 112 | testGoodKey, testGoodPub, ok = GenerateKey() 113 | if !ok { 114 | fmt.Println("Key generation failed") 115 | t.FailNow() 116 | } 117 | testPeerKey, testPeerPub, ok = GenerateKey() 118 | if !ok { 119 | fmt.Println("Key generation failed") 120 | t.FailNow() 121 | } 122 | testBadKey, testBadPub, ok = GenerateKey() 123 | if !ok { 124 | fmt.Println("Key generation failed") 125 | t.FailNow() 126 | } 127 | ioutil.WriteFile("testvectors/good.key", testGoodKey, 0644) 128 | ioutil.WriteFile("testvectors/good.pub", testGoodPub, 0644) 129 | ioutil.WriteFile("testvectors/peer.key", testPeerKey, 0644) 130 | ioutil.WriteFile("testvectors/peer.pub", testPeerPub, 0644) 131 | ioutil.WriteFile("testvectors/bad.key", testBadKey, 0644) 132 | ioutil.WriteFile("testvectors/bad.pub", testBadPub, 0644) 133 | } 134 | */ 135 | 136 | func TestBoxing(t *testing.T) { 137 | for i := 0; i < len(testMessages); i++ { 138 | box, ok := Seal([]byte(testMessages[i]), testPeerPub) 139 | if !ok { 140 | fmt.Println("Boxing failed: message", i) 141 | t.FailNow() 142 | } else if len(box) != len(testMessages[i])+Overhead { 143 | fmt.Println("The box length is invalid.") 144 | t.FailNow() 145 | } else if BoxIsSigned(box) { 146 | fmt.Println("IsBoxSigned should return false.") 147 | t.FailNow() 148 | } 149 | /* 150 | fileName := fmt.Sprintf("testvectors/test_vector-%d.bin", i+1) 151 | ioutil.WriteFile(fileName, []byte(testMessages[i]), 0644) 152 | fileName = fmt.Sprintf("testvectors/test_box-%d.bin", i+1) 153 | ioutil.WriteFile(fileName, box, 0644) 154 | */ 155 | testBoxes[i] = string(box) 156 | } 157 | } 158 | 159 | func TestUnboxing(t *testing.T) { 160 | for i := 0; i < len(testMessages); i++ { 161 | message, ok := Open([]byte(testBoxes[i]), testPeerKey) 162 | if !ok { 163 | fmt.Println("Unboxing failed: message", i) 164 | t.FailNow() 165 | } else if string(message) != testMessages[i] { 166 | fmt.Printf("Unboxing failed: expected '%s', got '%s'\n", 167 | testMessages[i], string(message)) 168 | t.FailNow() 169 | } 170 | } 171 | } 172 | 173 | func TestBadUnboxing(t *testing.T) { 174 | for i := 0; i < len(testMessages); i++ { 175 | _, ok := Open([]byte(testBoxes[i]), testBadKey) 176 | if ok { 177 | fmt.Println("Unboxing should have failed: message", i) 178 | t.FailNow() 179 | } 180 | _, ok = Open(mutate([]byte(testBoxes[i])), testGoodKey) 181 | if ok { 182 | fmt.Println("Unboxing should have failed: message", i) 183 | t.FailNow() 184 | } 185 | } 186 | } 187 | 188 | func TestSignedBoxing(t *testing.T) { 189 | for i := 0; i < len(testMessages); i++ { 190 | box, ok := SignAndSeal([]byte(testMessages[i]), testGoodKey, testGoodPub, testPeerPub) 191 | if !ok { 192 | fmt.Println("Boxing failed: message", i) 193 | t.FailNow() 194 | } else if len(box) < len(testMessages[i])+Overhead+sigSize { 195 | fmt.Println("The box length is invalid.") 196 | t.FailNow() 197 | } else if !BoxIsSigned(box) { 198 | fmt.Println("IsBoxSigned should return true.") 199 | t.FailNow() 200 | } 201 | /* 202 | fileName := fmt.Sprintf("testvectors/test_signed_vector-%d.bin", i+1) 203 | ioutil.WriteFile(fileName, []byte(testMessages[i]), 0644) 204 | fileName = fmt.Sprintf("testvectors/test_signed_box-%d.bin", i+1) 205 | ioutil.WriteFile(fileName, box, 0644) 206 | */ 207 | testBoxes[i] = string(box) 208 | } 209 | } 210 | 211 | func TestSignedUnboxing(t *testing.T) { 212 | for i := 0; i < len(testMessages); i++ { 213 | message, ok := OpenAndVerify([]byte(testBoxes[i]), testPeerKey, testGoodPub) 214 | if !ok { 215 | fmt.Println("Unboxing failed: message", i) 216 | t.FailNow() 217 | } else if string(message) != testMessages[i] { 218 | fmt.Printf("Unboxing failed: expected '%s', got '%s'\n", 219 | testMessages[i], string(message)) 220 | t.FailNow() 221 | } 222 | } 223 | } 224 | 225 | func TestSignedBadUnboxing(t *testing.T) { 226 | for i := 0; i < len(testMessages); i++ { 227 | _, ok := OpenAndVerify([]byte(testBoxes[i]), testPeerKey, testBadPub) 228 | if ok { 229 | fmt.Println("Unboxing should have failed: message", i) 230 | t.FailNow() 231 | } else if _, ok = OpenAndVerify(mutate([]byte(testBoxes[i])), testPeerKey, testGoodPub); ok { 232 | fmt.Println("Unboxing should have failed: message", i) 233 | t.FailNow() 234 | } else if _, ok = OpenAndVerify(mutate([]byte(testBoxes[i])), testPeerKey, testGoodPub); ok { 235 | fmt.Println("Unboxing should have failed: message", i) 236 | t.FailNow() 237 | } 238 | } 239 | } 240 | 241 | // TestLargerBox tests the encryption of a 4,026 byte test file. 242 | func TestLargerBox(t *testing.T) { 243 | var err error 244 | testBoxFile, err = ioutil.ReadFile("testdata/TEST.txt") 245 | if err != nil { 246 | fmt.Println("Failed to read test data:", err.Error()) 247 | t.FailNow() 248 | } 249 | 250 | box, ok := Seal(testBoxFile, testPeerPub) 251 | if !ok { 252 | fmt.Println("Failed to box message.") 253 | t.FailNow() 254 | } 255 | 256 | message, ok := Open(box, testPeerKey) 257 | if !ok { 258 | fmt.Println("Failed to unbox message.") 259 | t.FailNow() 260 | } 261 | 262 | if !bytes.Equal(message, testBoxFile) { 263 | fmt.Println("Recovered message is invalid.") 264 | t.FailNow() 265 | } 266 | } 267 | 268 | func TestKeySigning(t *testing.T) { 269 | sig, ok := SignKey(testGoodKey, testGoodPub, testPeerPub) 270 | if !ok { 271 | fmt.Println("Failed to sign key.") 272 | t.FailNow() 273 | } 274 | 275 | ok = VerifySignedKey(testPeerPub, testGoodPub, sig) 276 | if !ok { 277 | fmt.Println("Key signature validation failed.") 278 | t.FailNow() 279 | } 280 | 281 | ok = VerifySignedKey(testPeerPub, testBadPub, sig) 282 | if ok { 283 | fmt.Println("Key signature check should have failed.") 284 | t.FailNow() 285 | } 286 | 287 | ok = VerifySignedKey(testPeerPub, mutate(testGoodPub), sig) 288 | if ok { 289 | fmt.Println("Key signature check should have failed.") 290 | t.FailNow() 291 | } 292 | 293 | ok = VerifySignedKey(mutate(testPeerPub), testGoodPub, sig) 294 | if ok { 295 | fmt.Println("Key signature check should have failed.") 296 | t.FailNow() 297 | } 298 | } 299 | 300 | func BenchmarkUnsignedSeal(b *testing.B) { 301 | for i := 0; i < b.N; i++ { 302 | _, ok := Seal(testBoxFile, testPeerPub) 303 | if !ok { 304 | fmt.Println("Couldn't seal message: benchmark aborted.") 305 | b.FailNow() 306 | } 307 | } 308 | } 309 | 310 | func BenchmarkSignAndSeal(b *testing.B) { 311 | for i := 0; i < b.N; i++ { 312 | _, ok := SignAndSeal(testBoxFile, testGoodKey, testGoodPub, testPeerPub) 313 | if !ok { 314 | fmt.Println("Couldn't seal message: benchmark aborted.") 315 | b.FailNow() 316 | } 317 | } 318 | } 319 | 320 | // Benchmark the Open function, which retrieves a message from a box. 321 | func BenchmarkUnsignedOpen(b *testing.B) { 322 | box, ok := Seal(testBoxFile, testPeerPub) 323 | if !ok { 324 | fmt.Println("Can't seal message: benchmark aborted.") 325 | b.FailNow() 326 | } 327 | for i := 0; i < b.N; i++ { 328 | _, ok := Open(box, testPeerKey) 329 | if !ok { 330 | fmt.Println("Couldn't open message: benchmark aborted.") 331 | b.FailNow() 332 | } 333 | } 334 | } 335 | 336 | // Benchmark the Open function, which retrieves a message from a box. 337 | func BenchmarkOpenSigned(b *testing.B) { 338 | box, ok := SignAndSeal(testBoxFile, testGoodKey, testGoodPub, testPeerPub) 339 | if !ok { 340 | fmt.Println("Can't seal message: benchmark aborted.") 341 | b.FailNow() 342 | } 343 | for i := 0; i < b.N; i++ { 344 | _, ok := OpenAndVerify(box, testPeerKey, testGoodPub) 345 | if !ok { 346 | fmt.Println("Couldn't open message: benchmark aborted.") 347 | b.FailNow() 348 | } 349 | } 350 | } 351 | 352 | // Benchmark the SharedKey function. 353 | func BenchmarkSharedKey(b *testing.B) { 354 | for i := 0; i < b.N; i++ { 355 | _, ok := SharedKey(testGoodKey, testPeerPub) 356 | if !ok { 357 | fmt.Println("Computing shared key failed: benchmark aborted.") 358 | b.FailNow() 359 | } 360 | } 361 | } 362 | 363 | // Benchmark key signing. 364 | func BenchmarkKeySigning(b *testing.B) { 365 | for i := 0; i < b.N; i++ { 366 | _, ok := SignKey(testGoodKey, testGoodPub, testPeerPub) 367 | if !ok { 368 | fmt.Println("Key signing failed: benchmark aborted.") 369 | b.FailNow() 370 | } 371 | } 372 | } 373 | 374 | // Benchmark key signature verification. 375 | func BenchmarkKeyVerification(b *testing.B) { 376 | sig, ok := SignKey(testGoodKey, testGoodPub, testPeerPub) 377 | if !ok { 378 | fmt.Println("Key signing failed: benchmark aborted.") 379 | b.FailNow() 380 | } 381 | for i := 0; i < b.N; i++ { 382 | ok = VerifySignedKey(testPeerPub, testGoodPub, sig) 383 | if !ok { 384 | fmt.Println("Key signature verification failed: benchmark aborted.") 385 | b.FailNow() 386 | } 387 | } 388 | } 389 | 390 | /* 391 | func TestSharedKeyPairs(t *testing.T) { 392 | for i := 0; i < 4; i++ { 393 | p_priv, p_pub, ok := GenerateKey() 394 | if !ok { 395 | fmt.Println("Failed to generate peer key.") 396 | t.FailNow() 397 | } 398 | privFN := fmt.Sprintf("testvectors/peer_%d.key", i) 399 | pubFN := fmt.Sprintf("testvectors/peer_%d.pub", i) 400 | ioutil.WriteFile(privFN, p_priv, 0644) 401 | ioutil.WriteFile(pubFN, p_pub, 0644) 402 | } 403 | } 404 | */ 405 | 406 | var peerPublicList = []PublicKey{ 407 | PublicKey{ 408 | 0x04, 0x52, 0xd6, 0xaf, 0xe0, 0x86, 0xf4, 0xf3, 409 | 0xd2, 0x68, 0x4a, 0xab, 0x65, 0xad, 0x5a, 0xce, 410 | 0xb8, 0xba, 0xba, 0x52, 0x1e, 0x3b, 0x71, 0x4e, 411 | 0xc6, 0xd3, 0x90, 0xa5, 0x89, 0x8b, 0xf0, 0x55, 412 | 0xea, 0xa7, 0x4c, 0x8e, 0xb6, 0xd7, 0xb0, 0x6d, 413 | 0x28, 0x83, 0x67, 0x20, 0xa6, 0x32, 0xd9, 0xb5, 414 | 0x1f, 0x32, 0x08, 0xe9, 0x28, 0x86, 0x93, 0x05, 415 | 0x82, 0xd1, 0x20, 0x77, 0x53, 0x54, 0x9d, 0xee, 416 | 0xf1, 417 | }, 418 | PublicKey{ 419 | 0x04, 0x1d, 0xc0, 0x72, 0x5f, 0xd0, 0xcc, 0x54, 420 | 0xfe, 0xa2, 0xaa, 0x49, 0x43, 0xce, 0x4e, 0xb9, 421 | 0xce, 0xd0, 0x21, 0xa9, 0x4c, 0x75, 0x60, 0x6b, 422 | 0xc7, 0xf3, 0x74, 0xb4, 0xa8, 0xb7, 0x89, 0x79, 423 | 0x49, 0x29, 0xc1, 0x51, 0xc8, 0xab, 0x83, 0x88, 424 | 0x9b, 0x57, 0xcf, 0xd8, 0x72, 0x5b, 0xd1, 0x9f, 425 | 0x03, 0x6d, 0xb1, 0x0a, 0x8e, 0x0c, 0x57, 0xf6, 426 | 0x22, 0x14, 0xe9, 0xd2, 0xa9, 0xdf, 0x02, 0xdc, 427 | 0xf6, 428 | }, 429 | PublicKey{ 430 | 0x04, 0x1b, 0x81, 0x55, 0x66, 0x8e, 0x73, 0xa2, 431 | 0xd9, 0x07, 0xa1, 0xa7, 0xa0, 0xdc, 0xf3, 0xad, 432 | 0x85, 0xfc, 0xb5, 0xc6, 0xd5, 0x60, 0x5f, 0x3e, 433 | 0xef, 0xcc, 0x2f, 0x1f, 0xbb, 0xd4, 0x1e, 0x59, 434 | 0x9d, 0xce, 0xf2, 0x8d, 0x6d, 0x6d, 0xed, 0x6d, 435 | 0xd0, 0x55, 0x11, 0x20, 0x77, 0x79, 0xb6, 0x17, 436 | 0x5a, 0x99, 0xb0, 0xf6, 0x89, 0x8a, 0x83, 0x51, 437 | 0xf9, 0xcd, 0x51, 0x6f, 0xcb, 0x41, 0x32, 0x61, 438 | 0x1b, 439 | }, 440 | PublicKey{ 441 | 0x04, 0xb5, 0x91, 0x50, 0xf8, 0x24, 0xf3, 0xe3, 442 | 0x81, 0xb2, 0x63, 0xda, 0x19, 0xf1, 0x7b, 0x7f, 443 | 0x84, 0x83, 0x8b, 0xe9, 0x32, 0x5b, 0x11, 0xa9, 444 | 0xb6, 0xf4, 0xa3, 0x25, 0x34, 0xc9, 0xf1, 0x0b, 445 | 0x3e, 0x24, 0x6d, 0x2b, 0x9c, 0xa6, 0x34, 0x75, 446 | 0x8e, 0x66, 0x8b, 0xfc, 0x9b, 0xb6, 0x37, 0x2b, 447 | 0x10, 0xc0, 0xb5, 0x59, 0x6e, 0x5d, 0x53, 0xb5, 448 | 0x00, 0xc1, 0xde, 0xf5, 0xfb, 0x16, 0x91, 0x0c, 449 | 0xb4, 450 | }, 451 | } 452 | 453 | var peerPrivList = []PrivateKey{ 454 | PrivateKey{ 455 | 0x49, 0xe8, 0x9b, 0xa2, 0x65, 0x51, 0x51, 0x8a, 456 | 0xe5, 0xcb, 0xab, 0xfb, 0x0f, 0xe7, 0xa5, 0x9c, 457 | 0xea, 0x07, 0xbc, 0x39, 0x92, 0x3e, 0x09, 0x8e, 458 | 0xe7, 0x3d, 0x80, 0x19, 0xa6, 0xc4, 0x7e, 0x31, 459 | }, 460 | PrivateKey{ 461 | 0x6a, 0x28, 0xdb, 0x54, 0x09, 0x7f, 0xf4, 0x96, 462 | 0xe9, 0x21, 0x29, 0x4d, 0xee, 0xd9, 0x1b, 0xca, 463 | 0x43, 0xef, 0x55, 0x41, 0xae, 0xd3, 0xe1, 0xcf, 464 | 0x2a, 0xca, 0x69, 0xae, 0xd2, 0x16, 0x4c, 0xad, 465 | }, 466 | PrivateKey{ 467 | 0xdd, 0x5a, 0x32, 0x96, 0xff, 0x8d, 0x0c, 0xfd, 468 | 0xc4, 0x4b, 0x54, 0x17, 0x89, 0x87, 0xe0, 0xdf, 469 | 0xa6, 0x02, 0x81, 0x48, 0x47, 0xa6, 0x04, 0x0f, 470 | 0xb2, 0xad, 0xc1, 0x7b, 0xce, 0x41, 0x7d, 0xd6, 471 | }, 472 | PrivateKey{ 473 | 0x2d, 0xe4, 0x7f, 0xe0, 0xd5, 0xc6, 0xe4, 0xc4, 474 | 0xcb, 0x63, 0x60, 0xc5, 0xe3, 0xc6, 0x60, 0xf2, 475 | 0xd1, 0x0a, 0xf9, 0xe5, 0xff, 0xb4, 0xfc, 0x44, 476 | 0x6a, 0x62, 0x25, 0x41, 0x5b, 0x79, 0x88, 0x1e, 477 | }, 478 | } 479 | 480 | func TestSharedBoxing(t *testing.T) { 481 | for i := 0; i < len(testMessages); i++ { 482 | box, ok := SealShared([]byte(testMessages[i]), peerPublicList) 483 | if !ok { 484 | fmt.Println("Shared boxing failed: message", i) 485 | t.FailNow() 486 | } 487 | testBoxes[i] = string(box) 488 | /* 489 | fname := fmt.Sprintf("testvectors/test_shared_box_%d.bin", i) 490 | ioutil.WriteFile(fname, []byte(testBoxes[i]), 0644) 491 | */ 492 | } 493 | } 494 | 495 | func TestSharedUnboxing(t *testing.T) { 496 | for i := 0; i < len(testMessages); i++ { 497 | for kn := 0; kn < 4; kn++ { 498 | m, ok := OpenShared([]byte(testBoxes[i]), 499 | peerPrivList[kn], 500 | peerPublicList[kn]) 501 | if !ok { 502 | fmt.Println("Shared unboxing failed: message", i) 503 | fmt.Printf("box: %x\n", testBoxes[i]) 504 | t.FailNow() 505 | } else if string(m) != testMessages[i] { 506 | fmt.Println("Shared unboxing did not return same plaintext.") 507 | t.FailNow() 508 | } 509 | _, ok = OpenShared([]byte(testBoxes[i]), 510 | testPeerKey, testPeerPub) 511 | if ok { 512 | fmt.Println("Shared unboxing should have failed!") 513 | t.FailNow() 514 | } 515 | } 516 | _, ok := OpenShared(mutate([]byte(testBoxes[i])), 517 | peerPrivList[0], peerPublicList[0]) 518 | if ok { 519 | fmt.Println("Unboxing should have failed: message", i) 520 | t.FailNow() 521 | } 522 | } 523 | } 524 | 525 | func TestSharedSignedBoxing(t *testing.T) { 526 | for i := 0; i < len(testMessages); i++ { 527 | box, ok := SignAndSealShared([]byte(testMessages[i]), peerPublicList, testGoodKey, 528 | testGoodPub) 529 | if !ok { 530 | fmt.Println("Shared boxing failed: message", i) 531 | t.FailNow() 532 | } 533 | testBoxes[i] = string(box) 534 | /* 535 | fname := fmt.Sprintf("testvectors/test_shared_signed_box_%d.bin", i) 536 | ioutil.WriteFile(fname, []byte(testBoxes[i]), 0644) 537 | */ 538 | } 539 | } 540 | 541 | func TestSharedSignedUnboxing(t *testing.T) { 542 | for i := 0; i < len(testMessages); i++ { 543 | for kn := 0; kn < 4; kn++ { 544 | m, ok := OpenSharedAndVerify([]byte(testBoxes[i]), 545 | peerPrivList[kn], 546 | peerPublicList[kn], 547 | testGoodPub) 548 | if !ok { 549 | fmt.Println("Shared unboxing failed: message", i) 550 | fmt.Printf("box: %x\n", testBoxes[i]) 551 | t.FailNow() 552 | } else if string(m) != testMessages[i] { 553 | fmt.Println("Shared unboxing did not return same plaintext.") 554 | t.FailNow() 555 | } 556 | _, ok = OpenSharedAndVerify([]byte(testBoxes[i]), 557 | testPeerKey, testPeerPub, testGoodPub) 558 | if ok { 559 | fmt.Println("Shared unboxing should have failed!") 560 | t.FailNow() 561 | } 562 | _, ok = OpenSharedAndVerify([]byte(testBoxes[i]), 563 | peerPrivList[kn], 564 | peerPublicList[kn], 565 | testPeerPub) 566 | if ok { 567 | fmt.Println("Signature verification should have failed!") 568 | t.FailNow() 569 | } 570 | } 571 | _, ok := OpenSharedAndVerify(mutate([]byte(testBoxes[i])), 572 | peerPrivList[0], 573 | peerPublicList[0], 574 | testGoodPub) 575 | if ok { 576 | fmt.Println("Signature verification should have failed!") 577 | t.FailNow() 578 | } 579 | } 580 | } 581 | 582 | func BenchmarkSharedUnsignedSeal(b *testing.B) { 583 | for i := 0; i < b.N; i++ { 584 | _, ok := SealShared(testBoxFile, peerPublicList) 585 | if !ok { 586 | fmt.Println("Couldn't seal message: benchmark aborted.") 587 | b.FailNow() 588 | } 589 | } 590 | } 591 | 592 | func BenchmarkSharedSignAndSeal(b *testing.B) { 593 | for i := 0; i < b.N; i++ { 594 | _, ok := SignAndSealShared(testBoxFile, peerPublicList, testGoodKey, 595 | testGoodPub) 596 | if !ok { 597 | fmt.Println("Couldn't seal message: benchmark aborted.") 598 | b.FailNow() 599 | } 600 | } 601 | } 602 | 603 | // Benchmark the Open function, which retrieves a message from a box. 604 | func BenchmarkSharedUnsignedOpen(b *testing.B) { 605 | box, ok := SealShared(testBoxFile, peerPublicList) 606 | if !ok { 607 | fmt.Println("Can't seal message: benchmark aborted.") 608 | b.FailNow() 609 | } 610 | for i := 0; i < b.N; i++ { 611 | _, ok := OpenShared(box, peerPrivList[3], peerPublicList[3]) 612 | if !ok { 613 | fmt.Println("Couldn't open message: benchmark aborted.") 614 | b.FailNow() 615 | } 616 | } 617 | } 618 | 619 | // Benchmark the OpenSigned function, which retrieves a message from a box and verifies a 620 | // signature on it. 621 | func BenchmarkOpenSharedSigned(b *testing.B) { 622 | box, ok := SignAndSealShared(testBoxFile, peerPublicList, testGoodKey, testGoodPub) 623 | if !ok { 624 | fmt.Println("Can't seal message: benchmark aborted.") 625 | b.FailNow() 626 | } 627 | for i := 0; i < b.N; i++ { 628 | _, ok := OpenSharedAndVerify(box, peerPrivList[3], peerPublicList[3], testGoodPub) 629 | if !ok { 630 | fmt.Println("Couldn't open message: benchmark aborted.") 631 | b.FailNow() 632 | } 633 | } 634 | } 635 | -------------------------------------------------------------------------------- /stoutbox/stoutbox_test.go: -------------------------------------------------------------------------------- 1 | package stoutbox 2 | 3 | import "bytes" 4 | import "crypto/rand" 5 | import "fmt" 6 | import "io/ioutil" 7 | import "math/big" 8 | import "testing" 9 | 10 | var testMessages = []string{ 11 | "Hello, world.", 12 | "Yes... yes. This is a fertile land, and we will thrive. We will rule over all this land, and we will call it... This Land.", 13 | "Ah! Curse your sudden but inevitable betrayal!", 14 | "And I'm thinkin' you weren't burdened with an overabundance of schooling. So why don't we just ignore each other until we go away?", 15 | "Sir, I think you have a problem with your brain being missing.", 16 | "It's the way of life in my findings that journeys end when and where they want to; and that's where you make your home.", 17 | "I get confused. I remember everything. I remember too much. And... some of it's made up, and... some of it can't be quantified, and... there's secrets... and...", 18 | "Yeah, we're pretty much just giving each other significant glances and laughing incessantly.", 19 | "Jayne, go play with your rainstick.", 20 | } 21 | 22 | var ( 23 | testBoxes = make([]string, len(testMessages)) 24 | testBoxFile []byte 25 | testGoodKey = PrivateKey{ 26 | 0x01, 0xd2, 0x21, 0x46, 0x28, 0x73, 0x25, 0x7f, 27 | 0xf9, 0x74, 0x70, 0xe0, 0x65, 0xdd, 0xff, 0x18, 28 | 0x03, 0x0b, 0x41, 0x30, 0x67, 0x24, 0x50, 0x47, 29 | 0x6a, 0xe0, 0x44, 0xc7, 0x30, 0x05, 0xc1, 0x29, 30 | 0xf8, 0x56, 0x5e, 0x6f, 0x15, 0xac, 0x20, 0xc7, 31 | 0xe9, 0x32, 0xe1, 0x90, 0x15, 0xcc, 0xeb, 0x0a, 32 | 0x86, 0x02, 0x9a, 0x9f, 0x8d, 0x22, 0x37, 0xc6, 33 | 0x4f, 0x46, 0xde, 0xf0, 0x17, 0x26, 0x7f, 0x8a, 34 | 0xa2, 0x64, 35 | } 36 | testGoodPub = PublicKey{ 37 | 0x04, 0x00, 0xbc, 0xcd, 0x20, 0x7d, 0xba, 0xb0, 38 | 0x42, 0xd1, 0x41, 0x35, 0x8c, 0xd8, 0x26, 0x5d, 39 | 0x3d, 0x92, 0x2c, 0x28, 0x58, 0xbb, 0xfa, 0xbe, 40 | 0x67, 0xf8, 0xae, 0xd4, 0x01, 0xa8, 0xb7, 0x74, 41 | 0xe0, 0x5f, 0xfe, 0x7b, 0x8b, 0x0b, 0x8a, 0x31, 42 | 0x8e, 0xd8, 0x6d, 0xe6, 0xfe, 0x9e, 0xbb, 0x9f, 43 | 0x1a, 0x40, 0xe5, 0x91, 0x62, 0x0a, 0xe4, 0x12, 44 | 0xc8, 0xe2, 0x59, 0xa8, 0x14, 0xf8, 0xdc, 0x70, 45 | 0x4f, 0xb3, 0x73, 0x00, 0xc0, 0x29, 0x1e, 0x60, 46 | 0xd5, 0xa3, 0xe4, 0xcd, 0x90, 0x60, 0xae, 0x9d, 47 | 0x45, 0xfa, 0xa8, 0x0f, 0x7f, 0xab, 0xf8, 0x06, 48 | 0x54, 0xc4, 0xe4, 0x7e, 0x48, 0xe6, 0xa4, 0xea, 49 | 0x2b, 0xf5, 0x6a, 0xfd, 0x82, 0x35, 0x63, 0xe1, 50 | 0xe2, 0x04, 0xd5, 0xd7, 0xa1, 0x2e, 0x35, 0xcc, 51 | 0xdf, 0x8a, 0xa1, 0x54, 0xa9, 0xef, 0x01, 0xae, 52 | 0xde, 0x97, 0x1f, 0x3b, 0xa5, 0x68, 0xde, 0xd0, 53 | 0x58, 0xf8, 0xa3, 0x1e, 0xd9, 54 | } 55 | testPeerKey = PrivateKey{ 56 | 0x00, 0x6f, 0x1b, 0x51, 0x3e, 0x67, 0x24, 0x0f, 57 | 0xf7, 0x19, 0x77, 0x8d, 0x00, 0xe2, 0x94, 0xf7, 58 | 0x27, 0x5f, 0x67, 0x9b, 0x60, 0x8e, 0x08, 0xae, 59 | 0xc1, 0x0a, 0x79, 0x13, 0xc0, 0x21, 0x42, 0x52, 60 | 0xe4, 0xf0, 0x3c, 0xe1, 0x8a, 0xc2, 0x6b, 0x1d, 61 | 0x0a, 0x91, 0xb3, 0x19, 0xc2, 0xd6, 0x7b, 0x04, 62 | 0x25, 0xab, 0x5b, 0x9a, 0x1d, 0xd3, 0x76, 0xca, 63 | 0xd2, 0x9b, 0x4a, 0xdd, 0x60, 0x97, 0x31, 0x53, 64 | 0x8b, 0x8e, 65 | } 66 | testPeerPub = PublicKey{ 67 | 0x04, 0x00, 0x1a, 0x94, 0x55, 0x77, 0x4f, 0x1e, 68 | 0x47, 0x5a, 0x34, 0x81, 0xf7, 0xa0, 0x4a, 0xc9, 69 | 0x73, 0x61, 0x23, 0x8d, 0xda, 0x72, 0x2a, 0xd9, 70 | 0x0c, 0xd4, 0x14, 0x18, 0x29, 0x99, 0x23, 0xa3, 71 | 0x0c, 0x03, 0xde, 0x93, 0x63, 0x50, 0x55, 0x99, 72 | 0xa6, 0x6b, 0xa3, 0x1e, 0x45, 0xbb, 0xc8, 0xd1, 73 | 0x04, 0x5a, 0xe9, 0x9f, 0x28, 0xc1, 0xc6, 0x6e, 74 | 0xfc, 0x5e, 0xb3, 0x4a, 0x81, 0x9f, 0x61, 0x75, 75 | 0xdf, 0xa9, 0x89, 0x01, 0xd6, 0x29, 0x76, 0xe4, 76 | 0x3c, 0xf3, 0x2e, 0x43, 0xdb, 0x26, 0x0e, 0xf5, 77 | 0xab, 0xd4, 0x58, 0xc4, 0x5d, 0x8c, 0xe5, 0x0c, 78 | 0x5b, 0x02, 0xb7, 0xe2, 0x4c, 0xa3, 0x65, 0x88, 79 | 0x59, 0x75, 0xcf, 0x22, 0x1a, 0x94, 0x0f, 0x5d, 80 | 0xbb, 0x9f, 0xe0, 0x18, 0x01, 0xe9, 0x23, 0x4f, 81 | 0x72, 0xa9, 0x9a, 0x1b, 0x47, 0xa0, 0x9e, 0x55, 82 | 0x9f, 0x8a, 0xfb, 0x1c, 0xf0, 0x4f, 0xf8, 0xd4, 83 | 0x2e, 0xda, 0xa5, 0x23, 0x96, 84 | } 85 | testBadKey = PrivateKey{ 86 | 0x01, 0xe2, 0xc5, 0x9c, 0x6e, 0x6b, 0xf0, 0x19, 87 | 0x84, 0xfa, 0x0a, 0x2a, 0x2e, 0xbe, 0x9c, 0x13, 88 | 0xc8, 0x5e, 0x35, 0xc7, 0x85, 0x5f, 0x3b, 0xe9, 89 | 0x95, 0xfc, 0x03, 0x42, 0xe3, 0xd7, 0xa7, 0x49, 90 | 0xde, 0x45, 0xf1, 0xc5, 0x09, 0xbe, 0x3b, 0x22, 91 | 0x1e, 0x96, 0x8d, 0xa4, 0x71, 0x91, 0x3e, 0x89, 92 | 0x77, 0x9e, 0xcf, 0x3e, 0x7d, 0xf3, 0xf9, 0x37, 93 | 0x69, 0xe0, 0xd5, 0x51, 0x72, 0xcc, 0x84, 0x03, 94 | 0x39, 0x9b, 95 | } 96 | testBadPub = PublicKey{ 97 | 0x04, 0x00, 0x1e, 0x00, 0xa8, 0xcb, 0x1a, 0xd8, 98 | 0x7c, 0x9c, 0xcf, 0xd8, 0xdb, 0xf4, 0x27, 0xc4, 99 | 0x8a, 0x70, 0x01, 0x23, 0xbe, 0xd0, 0x37, 0x0e, 100 | 0x10, 0x8e, 0x07, 0x3c, 0x28, 0x7c, 0x45, 0x6f, 101 | 0xa8, 0x61, 0x3d, 0xf9, 0x64, 0xca, 0x20, 0xfb, 102 | 0xe9, 0x60, 0xea, 0x2a, 0xa2, 0xeb, 0x5a, 0x69, 103 | 0x98, 0x6d, 0x7e, 0x9f, 0x11, 0x10, 0x06, 0x27, 104 | 0x57, 0xcb, 0x4c, 0xf9, 0x72, 0x0e, 0x0f, 0x9b, 105 | 0xa2, 0xe8, 0x54, 0x00, 0x1f, 0x71, 0x78, 0xdd, 106 | 0x0a, 0xea, 0x5d, 0xe7, 0xda, 0xa8, 0x70, 0x3e, 107 | 0xa4, 0x42, 0x49, 0xa6, 0xb4, 0x94, 0x20, 0x36, 108 | 0x83, 0x35, 0xc8, 0xf7, 0x1b, 0xae, 0x76, 0x17, 109 | 0xfc, 0x02, 0x10, 0xa0, 0x65, 0x66, 0x19, 0x1c, 110 | 0x28, 0xc0, 0xed, 0x05, 0x00, 0x0c, 0xf2, 0x0b, 111 | 0x72, 0xe1, 0x4c, 0x45, 0x39, 0xb4, 0x88, 0xf1, 112 | 0xd8, 0xbd, 0xa7, 0xcd, 0xd3, 0x15, 0x14, 0xaf, 113 | 0xbf, 0x6b, 0x1a, 0xd9, 0x5f, 114 | } 115 | ) 116 | 117 | func randInt(max int64) int64 { 118 | maxBig := big.NewInt(max) 119 | n, err := rand.Int(PRNG, maxBig) 120 | if err != nil { 121 | return -1 122 | } 123 | return n.Int64() 124 | } 125 | 126 | func mutate(in []byte) (out []byte) { 127 | out = make([]byte, len(in)) 128 | copy(out, in) 129 | 130 | iterations := (randInt(int64(len(out))) / 2) + 1 131 | if iterations == -1 { 132 | panic("mutate failed") 133 | } 134 | for i := 0; i < int(iterations); i++ { 135 | mByte := randInt(int64(len(out))) 136 | mBit := randInt(7) 137 | if mBit == -1 || mByte == -1 { 138 | panic("mutate failed") 139 | } 140 | out[mByte] ^= (1 << uint(mBit)) 141 | } 142 | if bytes.Equal(out, in) { 143 | panic("mutate failed") 144 | } 145 | return out 146 | } 147 | 148 | /* 149 | func TestKeyGeneration(t *testing.T) { 150 | var ok bool 151 | testGoodKey, testGoodPub, ok = GenerateKey() 152 | if !ok { 153 | fmt.Println("key generation failed") 154 | t.FailNow() 155 | } 156 | testPeerKey, testPeerPub, ok = GenerateKey() 157 | if !ok { 158 | fmt.Println("key generation failed") 159 | t.FailNow() 160 | } 161 | testBadKey, testBadPub, ok = GenerateKey() 162 | if !ok { 163 | fmt.Println("key generation failed") 164 | t.FailNow() 165 | } 166 | ioutil.WriteFile("testvectors/good.key", testGoodKey, 0644) 167 | ioutil.WriteFile("testvectors/good.pub", testGoodPub, 0644) 168 | ioutil.WriteFile("testvectors/peer.key", testPeerKey, 0644) 169 | ioutil.WriteFile("testvectors/peer.pub", testPeerPub, 0644) 170 | ioutil.WriteFile("testvectors/bad.key", testBadKey, 0644) 171 | ioutil.WriteFile("testvectors/bad.pub", testBadPub, 0644) 172 | } 173 | */ 174 | 175 | func TestBoxing(t *testing.T) { 176 | for i := 0; i < len(testMessages); i++ { 177 | box, ok := Seal([]byte(testMessages[i]), testPeerPub) 178 | if !ok { 179 | fmt.Println("Boxing failed: message", i) 180 | t.FailNow() 181 | } else if len(box) != len(testMessages[i])+Overhead { 182 | fmt.Println("The box length is invalid.") 183 | t.FailNow() 184 | } 185 | testBoxes[i] = string(box) 186 | /* 187 | fileName := fmt.Sprintf("testvectors/test_vector-%d.bin", i+1) 188 | ioutil.WriteFile(fileName, []byte(testMessages[i]), 0644) 189 | fileName = fmt.Sprintf("testvectors/test_box-%d.bin", i+1) 190 | ioutil.WriteFile(fileName, box, 0644) 191 | */ 192 | } 193 | } 194 | 195 | func TestUnboxing(t *testing.T) { 196 | for i := 0; i < len(testMessages); i++ { 197 | message, ok := Open([]byte(testBoxes[i]), testPeerKey) 198 | if !ok { 199 | fmt.Println("Unboxing failed: message", i) 200 | t.FailNow() 201 | } else if string(message) != testMessages[i] { 202 | fmt.Printf("Unboxing failed: expected '%s', got '%s'\n", 203 | testMessages[i], string(message)) 204 | t.FailNow() 205 | } 206 | } 207 | } 208 | 209 | func TestBadUnboxing(t *testing.T) { 210 | for i := 0; i < len(testMessages); i++ { 211 | _, ok := Open([]byte(testBoxes[i]), testBadKey) 212 | if ok { 213 | fmt.Println("Unboxing should have failed: message", i) 214 | t.FailNow() 215 | } 216 | _, ok = Open(mutate([]byte(testBoxes[i])), testGoodKey) 217 | if ok { 218 | fmt.Println("Unboxing should have failed: message", i) 219 | t.FailNow() 220 | } 221 | } 222 | } 223 | 224 | func TestSignedBoxing(t *testing.T) { 225 | for i := 0; i < len(testMessages); i++ { 226 | box, ok := SignAndSeal([]byte(testMessages[i]), testGoodKey, testGoodPub, testPeerPub) 227 | if !ok { 228 | fmt.Println("Signed boxing failed: message", i) 229 | t.FailNow() 230 | } else if len(box) < len(testMessages[i])+Overhead+sigSize { 231 | fmt.Println("The box length is invalid.") 232 | t.FailNow() 233 | } 234 | testBoxes[i] = string(box) 235 | /* 236 | fileName := fmt.Sprintf("testvectors/test_signed_vector-%d.bin", i+1) 237 | ioutil.WriteFile(fileName, []byte(testMessages[i]), 0644) 238 | fileName = fmt.Sprintf("testvectors/test_signed_box-%d.bin", i+1) 239 | ioutil.WriteFile(fileName, box, 0644) 240 | */ 241 | } 242 | } 243 | 244 | func TestSignedUnboxing(t *testing.T) { 245 | for i := 0; i < len(testMessages); i++ { 246 | message, ok := OpenAndVerify([]byte(testBoxes[i]), testPeerKey, testGoodPub) 247 | if !ok { 248 | fmt.Println("Signed unboxing failed: message", i) 249 | t.FailNow() 250 | } else if string(message) != testMessages[i] { 251 | fmt.Printf("Signed unboxing failed: expected '%s', got '%s'\n", 252 | testMessages[i], string(message)) 253 | t.FailNow() 254 | } 255 | } 256 | } 257 | 258 | func TestSignedBadUnboxing(t *testing.T) { 259 | for i := 0; i < len(testMessages); i++ { 260 | _, ok := OpenAndVerify([]byte(testBoxes[i]), testPeerKey, testBadPub) 261 | if ok { 262 | fmt.Println("Unboxing should have failed: message", i) 263 | t.FailNow() 264 | } else if _, ok = OpenAndVerify(mutate([]byte(testBoxes[i])), testPeerKey, testGoodPub); ok { 265 | fmt.Println("Unboxing should have failed: message", i) 266 | t.FailNow() 267 | } else if _, ok = OpenAndVerify(mutate([]byte(testBoxes[i])), testPeerKey, testGoodPub); ok { 268 | fmt.Println("Unboxing should have failed: message", i) 269 | t.FailNow() 270 | } 271 | } 272 | } 273 | 274 | // TestLargerBox tests the encryption of a 4,026 byte test file. 275 | func TestLargerBox(t *testing.T) { 276 | var err error 277 | testBoxFile, err = ioutil.ReadFile("testdata/TEST.txt") 278 | if err != nil { 279 | fmt.Println("Failed to read test data:", err.Error()) 280 | t.FailNow() 281 | } 282 | 283 | box, ok := Seal(testBoxFile, testPeerPub) 284 | if !ok { 285 | fmt.Println("Failed to box message.") 286 | t.FailNow() 287 | } 288 | 289 | message, ok := Open(box, testPeerKey) 290 | if !ok { 291 | fmt.Println("Failed to unbox message.") 292 | t.FailNow() 293 | } 294 | 295 | if !bytes.Equal(message, testBoxFile) { 296 | fmt.Println("Recovered message is invalid.") 297 | t.FailNow() 298 | } 299 | } 300 | 301 | func BenchmarkUnsignedSeal(b *testing.B) { 302 | for i := 0; i < b.N; i++ { 303 | _, ok := Seal(testBoxFile, testPeerPub) 304 | if !ok { 305 | fmt.Println("Couldn't seal message: benchmark aborted.") 306 | b.FailNow() 307 | } 308 | } 309 | } 310 | 311 | func BenchmarkSignAndSeal(b *testing.B) { 312 | for i := 0; i < b.N; i++ { 313 | _, ok := SignAndSeal(testBoxFile, testGoodKey, testGoodPub, testPeerPub) 314 | if !ok { 315 | fmt.Println("Couldn't seal message: benchmark aborted.") 316 | b.FailNow() 317 | } 318 | } 319 | } 320 | 321 | // Benchmark the Open function, which retrieves a message from a box. 322 | func BenchmarkUnsignedOpen(b *testing.B) { 323 | box, ok := Seal(testBoxFile, testPeerPub) 324 | if !ok { 325 | fmt.Println("Can't seal message: benchmark aborted.") 326 | b.FailNow() 327 | } 328 | for i := 0; i < b.N; i++ { 329 | _, ok := Open(box, testPeerKey) 330 | if !ok { 331 | fmt.Println("Couldn't open message: benchmark aborted.") 332 | b.FailNow() 333 | } 334 | } 335 | } 336 | 337 | // Benchmark the OpenSigned function, which retrieves a message from a box and verifies a 338 | // signature on it. 339 | func BenchmarkOpenSigned(b *testing.B) { 340 | box, ok := SignAndSeal(testBoxFile, testGoodKey, testGoodPub, testPeerPub) 341 | if !ok { 342 | fmt.Println("Can't seal message: benchmark aborted.") 343 | b.FailNow() 344 | } 345 | for i := 0; i < b.N; i++ { 346 | _, ok := OpenAndVerify(box, testPeerKey, testGoodPub) 347 | if !ok { 348 | fmt.Println("Couldn't open message: benchmark aborted.") 349 | b.FailNow() 350 | } 351 | } 352 | } 353 | 354 | // Benchmark the SharedKey function. 355 | func BenchmarkSharedKey(b *testing.B) { 356 | for i := 0; i < b.N; i++ { 357 | _, ok := SharedKey(testGoodKey, testPeerPub) 358 | if !ok { 359 | fmt.Println("Computing shared key failed: benchmark aborted.") 360 | b.FailNow() 361 | } 362 | } 363 | } 364 | 365 | /* 366 | func TestSharedKeyPairs(t *testing.T) { 367 | for i := 0; i < 4; i++ { 368 | p_priv, p_pub, ok := GenerateKey() 369 | if !ok { 370 | fmt.Println("Failed to generate peer key.") 371 | t.FailNow() 372 | } 373 | privFN := fmt.Sprintf("testvectors/peer_%d.key", i) 374 | pubFN := fmt.Sprintf("testvectors/peer_%d.pub", i) 375 | ioutil.WriteFile(privFN, p_priv, 0644) 376 | ioutil.WriteFile(pubFN, p_pub, 0644) 377 | } 378 | } 379 | */ 380 | 381 | var peerPublicList = []PublicKey{ 382 | PublicKey{ 383 | 0x04, 0x00, 0x13, 0xd9, 0xcc, 0x7c, 0x96, 0x06, 384 | 0x6f, 0x2d, 0x45, 0x5b, 0xda, 0x81, 0x36, 0xaf, 385 | 0x26, 0xa0, 0x6b, 0x31, 0x88, 0x90, 0x69, 0x30, 386 | 0x0a, 0x9d, 0x7d, 0xd5, 0x35, 0x9c, 0xd4, 0x94, 387 | 0x85, 0xf9, 0x34, 0x16, 0x73, 0x2c, 0xda, 0x16, 388 | 0x35, 0x4d, 0xdd, 0x45, 0xf4, 0x52, 0xdf, 0x9b, 389 | 0x57, 0xfa, 0x51, 0x00, 0x11, 0xcb, 0x33, 0x47, 390 | 0x66, 0xb4, 0x79, 0xe3, 0x68, 0x4b, 0xf6, 0x1f, 391 | 0xf3, 0x9b, 0xdd, 0x00, 0xd3, 0x54, 0x86, 0xb3, 392 | 0xa1, 0x0b, 0x59, 0x6f, 0x21, 0x54, 0xe7, 0x5b, 393 | 0x04, 0xab, 0x3a, 0xbf, 0xc7, 0xee, 0x16, 0x52, 394 | 0xc4, 0xf7, 0x57, 0xbd, 0xae, 0x23, 0x5b, 0xa1, 395 | 0x6d, 0xf2, 0x25, 0x14, 0xcc, 0xe5, 0x75, 0x9f, 396 | 0x70, 0xd6, 0x79, 0x66, 0xa6, 0x2a, 0x72, 0xe8, 397 | 0x4f, 0x9c, 0x1e, 0xdc, 0x8a, 0xaa, 0x10, 0x8a, 398 | 0xb9, 0x49, 0xd1, 0xdb, 0x1f, 0xad, 0xd8, 0xf1, 399 | 0x82, 0xe9, 0xf5, 0x64, 0x9f, 400 | }, 401 | PublicKey{ 402 | 0x04, 0x00, 0x71, 0x45, 0x3b, 0x48, 0x56, 0x13, 403 | 0xad, 0x7a, 0xa4, 0xb9, 0xd4, 0xed, 0x74, 0x16, 404 | 0x45, 0x54, 0x38, 0xdf, 0x94, 0xba, 0xf7, 0x27, 405 | 0x1b, 0x2f, 0x52, 0xe0, 0x31, 0xf9, 0xa0, 0x1d, 406 | 0x19, 0xd5, 0x0b, 0x5d, 0xda, 0x21, 0xc6, 0x7a, 407 | 0x01, 0xf0, 0xdf, 0x1e, 0xbf, 0xf2, 0xe6, 0x48, 408 | 0xde, 0x71, 0xd9, 0x60, 0x12, 0x0d, 0x0f, 0xdc, 409 | 0x40, 0x08, 0x54, 0xd6, 0x29, 0xac, 0x9b, 0xd5, 410 | 0xc5, 0x97, 0xf6, 0x00, 0xf4, 0xff, 0xe6, 0x9f, 411 | 0xa3, 0x41, 0x9e, 0x5e, 0x2c, 0xbd, 0xb4, 0x40, 412 | 0x08, 0xb4, 0x24, 0xa5, 0x48, 0x74, 0xe8, 0xa4, 413 | 0x9f, 0x65, 0x42, 0xc0, 0x5e, 0x45, 0x11, 0xf7, 414 | 0x74, 0xf9, 0x89, 0x26, 0xf3, 0x8e, 0x42, 0x82, 415 | 0x6b, 0x4c, 0xe4, 0xe5, 0xaa, 0x26, 0x23, 0x24, 416 | 0x77, 0x53, 0x1d, 0x2f, 0xa2, 0x56, 0x67, 0x6e, 417 | 0x0b, 0xb5, 0x22, 0xd1, 0x86, 0x81, 0x33, 0xfa, 418 | 0x6d, 0xe3, 0xbf, 0xeb, 0x94, 419 | }, 420 | PublicKey{ 421 | 0x04, 0x01, 0xd6, 0x5b, 0xe9, 0x99, 0xda, 0x7e, 422 | 0x02, 0x25, 0x54, 0x98, 0xb4, 0x49, 0xf4, 0x0f, 423 | 0x45, 0x3b, 0xcf, 0xa2, 0x21, 0x75, 0x58, 0x1a, 424 | 0xbf, 0x45, 0xaf, 0x64, 0xa9, 0xef, 0x19, 0x22, 425 | 0xb8, 0xb2, 0x5e, 0x4e, 0xb2, 0x0a, 0x62, 0x97, 426 | 0x20, 0x62, 0xa4, 0x6f, 0x10, 0x34, 0xc7, 0x27, 427 | 0xc1, 0x88, 0x39, 0xdf, 0x40, 0x78, 0xd3, 0x65, 428 | 0x43, 0x3c, 0x68, 0xe3, 0x30, 0xb6, 0x1b, 0x06, 429 | 0x8b, 0x55, 0x8b, 0x01, 0x71, 0xd4, 0xa6, 0x07, 430 | 0x00, 0xb9, 0xa4, 0x2d, 0xbe, 0x26, 0x97, 0x2f, 431 | 0xe7, 0xf1, 0x0b, 0x1a, 0xf8, 0x43, 0x93, 0x0c, 432 | 0xde, 0xdf, 0x25, 0x84, 0x22, 0x69, 0xe9, 0x6e, 433 | 0xcc, 0x5a, 0x6c, 0x1e, 0x8b, 0x34, 0xdb, 0x6f, 434 | 0xa5, 0x52, 0xd8, 0x59, 0x39, 0x00, 0xec, 0x2e, 435 | 0xc0, 0x49, 0x92, 0xfb, 0xe9, 0x4b, 0x67, 0x97, 436 | 0xd6, 0xbd, 0x1d, 0x47, 0xdd, 0xff, 0xa6, 0xee, 437 | 0xd7, 0x22, 0xac, 0x57, 0xb6, 438 | }, 439 | PublicKey{ 440 | 0x04, 0x01, 0xf6, 0x2c, 0x3a, 0x95, 0x72, 0x4a, 441 | 0x42, 0x0c, 0x18, 0xbc, 0x3b, 0xa5, 0x70, 0x6f, 442 | 0xfa, 0x8c, 0xde, 0x41, 0xf3, 0x41, 0x49, 0x7c, 443 | 0xff, 0x2b, 0x26, 0x00, 0xef, 0x48, 0x63, 0xa6, 444 | 0x08, 0x64, 0x93, 0xc3, 0x10, 0x0b, 0x07, 0x79, 445 | 0xf9, 0x1b, 0x60, 0x60, 0xc1, 0xbf, 0x9b, 0x20, 446 | 0xaa, 0x0e, 0xc7, 0xc2, 0x64, 0x9d, 0xab, 0xfb, 447 | 0x69, 0xbf, 0xc1, 0x68, 0xa5, 0xb7, 0x14, 0xef, 448 | 0xc3, 0x64, 0x5c, 0x00, 0x29, 0x07, 0xaf, 0x35, 449 | 0x48, 0x07, 0x6d, 0x6a, 0xe4, 0x10, 0xc3, 0x4a, 450 | 0xae, 0x2c, 0xb7, 0x79, 0x06, 0x62, 0xd2, 0x98, 451 | 0xcc, 0xb1, 0x3c, 0x24, 0x05, 0xc5, 0xbf, 0xe3, 452 | 0xcb, 0xcb, 0xd5, 0xf2, 0x5e, 0xf7, 0x62, 0x30, 453 | 0x8c, 0x75, 0x0f, 0xe2, 0xce, 0xee, 0x61, 0x20, 454 | 0x4d, 0xf5, 0x3e, 0xe7, 0xce, 0xbf, 0x09, 0x68, 455 | 0x9c, 0x0e, 0xf5, 0x76, 0x64, 0xc3, 0x4e, 0x35, 456 | 0x54, 0x4d, 0x13, 0xd5, 0x58, 457 | }, 458 | } 459 | 460 | var peerPrivList = []PrivateKey{ 461 | PrivateKey{ 462 | 0x00, 0x83, 0x32, 0x4d, 0x6c, 0x29, 0x61, 0xc0, 463 | 0xe1, 0x5c, 0xd3, 0x32, 0x1f, 0x37, 0x28, 0xc1, 464 | 0x9b, 0x6a, 0x3e, 0x81, 0xe6, 0xfb, 0xb6, 0x19, 465 | 0x7d, 0x9a, 0x6d, 0x7e, 0x16, 0x09, 0xff, 0x19, 466 | 0xbc, 0x54, 0xc3, 0x8a, 0x13, 0xae, 0x5c, 0xb9, 467 | 0x29, 0xc5, 0x76, 0x3e, 0x41, 0xa1, 0xaa, 0xbf, 468 | 0x25, 0x5a, 0x81, 0x56, 0x07, 0x2b, 0xe9, 0x97, 469 | 0x7a, 0xad, 0xb5, 0x25, 0x80, 0x65, 0x10, 0x74, 470 | 0x1b, 0x60, 471 | }, 472 | PrivateKey{ 473 | 0x01, 0x39, 0xe3, 0x43, 0x2c, 0x6c, 0x19, 0x12, 474 | 0x51, 0xa9, 0xc7, 0x5c, 0x01, 0xe1, 0xe5, 0x0c, 475 | 0x4a, 0x8f, 0xc5, 0xf8, 0x02, 0x75, 0x2b, 0x94, 476 | 0x1b, 0xc8, 0x70, 0x07, 0x22, 0x58, 0xdc, 0x2f, 477 | 0x18, 0xd7, 0xef, 0xd3, 0x7a, 0x53, 0x91, 0xf7, 478 | 0xaa, 0x0d, 0xa3, 0xae, 0x8b, 0xdc, 0x4b, 0xc1, 479 | 0xdb, 0x42, 0xa3, 0x3c, 0x9c, 0x38, 0x6f, 0xf8, 480 | 0x64, 0xdf, 0x4f, 0x80, 0x72, 0x1f, 0xa6, 0x9f, 481 | 0x66, 0x5e, 482 | }, 483 | PrivateKey{ 484 | 0x01, 0x8b, 0x48, 0x3b, 0x24, 0xbb, 0x77, 0x65, 485 | 0x2e, 0xa1, 0x47, 0x15, 0xc1, 0x3d, 0x50, 0x19, 486 | 0xb7, 0x14, 0x4b, 0x3d, 0x7c, 0x21, 0x4c, 0x4c, 487 | 0x97, 0x80, 0x14, 0x07, 0x52, 0x27, 0xb8, 0xe3, 488 | 0x1a, 0x3c, 0x04, 0x3b, 0x0a, 0x43, 0xfd, 0x7f, 489 | 0xd5, 0xa6, 0xd1, 0x0e, 0x5a, 0x55, 0x17, 0x7b, 490 | 0x03, 0xf4, 0x5d, 0x42, 0x91, 0x63, 0xca, 0x32, 491 | 0x53, 0xe6, 0x92, 0xe7, 0x98, 0x27, 0x4c, 0x4d, 492 | 0x47, 0x8c, 493 | }, 494 | PrivateKey{ 495 | 0x00, 0x0b, 0x32, 0xf4, 0xe0, 0x1c, 0x77, 0xc9, 496 | 0x21, 0x99, 0xfe, 0xba, 0xac, 0xe5, 0xb8, 0xfd, 497 | 0xd2, 0x93, 0xa0, 0x66, 0xba, 0x7a, 0x58, 0x5d, 498 | 0x2e, 0xe0, 0x29, 0x4d, 0x77, 0xb5, 0x24, 0xb1, 499 | 0x70, 0xda, 0x4d, 0xb4, 0xa7, 0x54, 0x3f, 0x46, 500 | 0x66, 0x1d, 0x46, 0x65, 0xb8, 0x23, 0x49, 0x76, 501 | 0xad, 0x17, 0x10, 0xbe, 0x40, 0xee, 0x95, 0x32, 502 | 0xb0, 0x97, 0x74, 0xf9, 0x3e, 0x8d, 0x6e, 0xc2, 503 | 0xc9, 0xd0, 504 | }, 505 | } 506 | 507 | func TestSharedBoxing(t *testing.T) { 508 | for i := 0; i < len(testMessages); i++ { 509 | box, ok := SealShared([]byte(testMessages[i]), peerPublicList) 510 | if !ok { 511 | fmt.Println("Shared boxing failed: message", i) 512 | t.FailNow() 513 | } 514 | testBoxes[i] = string(box) 515 | } 516 | } 517 | 518 | func TestSharedUnboxing(t *testing.T) { 519 | for i := 0; i < len(testMessages); i++ { 520 | for kn := 0; kn < 4; kn++ { 521 | m, ok := OpenShared([]byte(testBoxes[i]), 522 | peerPrivList[kn], 523 | peerPublicList[kn]) 524 | if !ok { 525 | fmt.Println("Shared unboxing failed: message", i) 526 | fmt.Printf("box: %x\n", testBoxes[i]) 527 | t.FailNow() 528 | } else if string(m) != testMessages[i] { 529 | fmt.Println("Shared unboxing did not return same plaintext.") 530 | t.FailNow() 531 | } 532 | _, ok = OpenShared([]byte(testBoxes[i]), 533 | testPeerKey, testPeerPub) 534 | if ok { 535 | fmt.Println("Shared unboxing should have failed!") 536 | t.FailNow() 537 | } 538 | } 539 | _, ok := OpenShared(mutate([]byte(testBoxes[i])), 540 | peerPrivList[0], peerPublicList[0]) 541 | if ok { 542 | fmt.Println("Unboxing should have failed: message", i) 543 | t.FailNow() 544 | } 545 | } 546 | } 547 | 548 | func TestSharedSignedBoxing(t *testing.T) { 549 | for i := 0; i < len(testMessages); i++ { 550 | box, ok := SignAndSealShared([]byte(testMessages[i]), peerPublicList, testGoodKey, 551 | testGoodPub) 552 | if !ok { 553 | fmt.Println("Shared boxing failed: message", i) 554 | t.FailNow() 555 | } 556 | testBoxes[i] = string(box) 557 | } 558 | } 559 | 560 | func TestSharedSignedUnboxing(t *testing.T) { 561 | for i := 0; i < len(testMessages); i++ { 562 | for kn := 0; kn < 4; kn++ { 563 | m, ok := OpenSharedAndVerify([]byte(testBoxes[i]), 564 | peerPrivList[kn], 565 | peerPublicList[kn], 566 | testGoodPub) 567 | if !ok { 568 | fmt.Println("Shared unboxing failed: message", i) 569 | fmt.Printf("box: %x\n", testBoxes[i]) 570 | t.FailNow() 571 | } else if string(m) != testMessages[i] { 572 | fmt.Println("Shared unboxing did not return same plaintext.") 573 | t.FailNow() 574 | } 575 | _, ok = OpenSharedAndVerify([]byte(testBoxes[i]), 576 | testPeerKey, testPeerPub, testGoodPub) 577 | if ok { 578 | fmt.Println("Shared unboxing should have failed!") 579 | t.FailNow() 580 | } 581 | _, ok = OpenSharedAndVerify([]byte(testBoxes[i]), 582 | peerPrivList[kn], 583 | peerPublicList[kn], 584 | testPeerPub) 585 | if ok { 586 | fmt.Println("Signature verification should have failed!") 587 | t.FailNow() 588 | } 589 | } 590 | _, ok := OpenSharedAndVerify(mutate([]byte(testBoxes[i])), 591 | peerPrivList[0], 592 | peerPublicList[0], 593 | testPeerPub) 594 | if ok { 595 | fmt.Println("Signature verification should have failed!") 596 | t.FailNow() 597 | } 598 | } 599 | } 600 | 601 | func BenchmarkSharedUnsignedSeal(b *testing.B) { 602 | for i := 0; i < b.N; i++ { 603 | _, ok := SealShared(testBoxFile, peerPublicList) 604 | if !ok { 605 | fmt.Println("Couldn't seal message: benchmark aborted.") 606 | b.FailNow() 607 | } 608 | } 609 | } 610 | 611 | func BenchmarkSharedSignAndSeal(b *testing.B) { 612 | for i := 0; i < b.N; i++ { 613 | _, ok := SignAndSealShared(testBoxFile, peerPublicList, testGoodKey, 614 | testGoodPub) 615 | if !ok { 616 | fmt.Println("Couldn't seal message: benchmark aborted.") 617 | b.FailNow() 618 | } 619 | } 620 | } 621 | 622 | // Benchmark the Open function, which retrieves a message from a box. 623 | func BenchmarkSharedUnsignedOpen(b *testing.B) { 624 | box, ok := SealShared(testBoxFile, peerPublicList) 625 | if !ok { 626 | fmt.Println("Can't seal message: benchmark aborted.") 627 | b.FailNow() 628 | } 629 | for i := 0; i < b.N; i++ { 630 | _, ok := OpenShared(box, peerPrivList[3], peerPublicList[3]) 631 | if !ok { 632 | fmt.Println("Couldn't open message: benchmark aborted.") 633 | b.FailNow() 634 | } 635 | } 636 | } 637 | 638 | // Benchmark the OpenSigned function, which retrieves a message from a box and verifies a 639 | // signature on it. 640 | func BenchmarkOpenSharedSigned(b *testing.B) { 641 | box, ok := SignAndSealShared(testBoxFile, peerPublicList, testGoodKey, testGoodPub) 642 | if !ok { 643 | fmt.Println("Can't seal message: benchmark aborted.") 644 | b.FailNow() 645 | } 646 | for i := 0; i < b.N; i++ { 647 | _, ok := OpenSharedAndVerify(box, peerPrivList[3], peerPublicList[3], testGoodPub) 648 | if !ok { 649 | fmt.Println("Couldn't open message: benchmark aborted.") 650 | b.FailNow() 651 | } 652 | } 653 | } 654 | --------------------------------------------------------------------------------