func main() { message := []byte("Gophers of the world, unite!") alicePublic, alicePrivate, err := box.GenerateKey(rand.Reader) if err != nil { fmt.Printf("Failed to generate keypair for Alice: %v\n", err) return } bobPublic, bobPrivate, err := box.GenerateKey(rand.Reader) if err != nil { fmt.Printf("Failed to generate keypair for Bob: %v\n", err) return } encrypted := box.Seal(nil, message, &nonce, bobPublic, alicePrivate) decrypted, ok := box.Open(nil, encrypted, &nonce, alicePublic, bobPrivate) if !ok { fmt.Println("Decryption failed.\n") return } if !bytes.Equal(message, decrypted) { fmt.Println("Message recovered failed.\n") return } // Nonce should only be used once. updateNonce(&nonce) fmt.Println("OK") }
func main() { var rs [256][4][]byte for i := range rs { pk1, _, err := box.GenerateKey(rand.Reader) if err != nil { panic(err) } _, sk2, err := box.GenerateKey(rand.Reader) if err != nil { panic(err) } var n [24]byte // zero nonce m := make([]byte, i) if _, err := io.ReadFull(rand.Reader, m[:]); err != nil { panic(err) } b := box.Seal(nil, m, &n, pk1, sk2) rs[i][0] = pk1[:] rs[i][1] = sk2[:] rs[i][2] = m rs[i][3] = b } out, err := json.MarshalIndent(rs, "", "") if err != nil { panic(err) } fmt.Print("module.exports = ") fmt.Print(string(out)) fmt.Println(";") }
func (c *client) send(to *Contact, message *pond.Message) error { var nonce [24]byte c.randBytes(nonce[:]) messageBytes, err := proto.Marshal(message) if err != nil { return err } if len(messageBytes) > pond.MaxSerializedMessage { return errors.New("message too large") } plaintext := make([]byte, pond.MaxSerializedMessage+4) binary.LittleEndian.PutUint32(plaintext, uint32(len(messageBytes))) copy(plaintext[4:], messageBytes) c.randBytes(plaintext[4+len(messageBytes):]) sealed := make([]byte, len(plaintext)+box.Overhead+len(nonce)) copy(sealed, nonce[:]) box.Seal(sealed[len(nonce):len(nonce)], plaintext, &nonce, &to.theirCurrentDHPublic, &to.lastDHPrivate) sha := sha256.New() sha.Write(sealed) digest := sha.Sum(nil) sha.Reset() groupSig, err := to.myGroupKey.Sign(c.rand, digest, sha) if err != nil { return err } request := &pond.Request{ Deliver: &pond.Delivery{ To: to.theirIdentityPublic[:], Signature: groupSig, Generation: proto.Uint32(to.generation), Message: sealed, }, } out := &queuedMessage{ request: request, id: *message.Id, to: to.id, server: to.theirServer, message: message, created: time.Unix(*message.Time, 0), } c.enqueue(out) if len(message.Body) > 0 { c.outboxUI.Add(*message.Id, to.name, out.created.Format(shortTimeFormat), indicatorRed) } c.outbox = append(c.outbox, out) return nil }
func (cr *Crypto) Encrypt(data []byte) ([]byte, error) { ephemeralPublicKey, ephemeralPrivateKey, err := box.GenerateKey(rand.Reader) if err != nil { return nil, err } ciphertext := box.Seal(nil, data, &cr.nonce, &cr.publicKey, ephemeralPrivateKey) output := make([]byte, 32+len(ciphertext)) copy(output[0:32], ephemeralPublicKey[0:32]) copy(output[32:], ciphertext) return output, nil }
func (enc *boxEncoder) newCaveatId(cav Caveat, rootKey []byte, thirdPartyPub *PublicKey) (*caveatId, error) { var nonce [NonceLen]byte if _, err := rand.Read(nonce[:]); err != nil { return nil, fmt.Errorf("cannot generate random number for nonce: %v", err) } plain := caveatIdRecord{ RootKey: rootKey, Condition: cav.Condition, } plainData, err := json.Marshal(&plain) if err != nil { return nil, fmt.Errorf("cannot marshal %#v: %v", &plain, err) } sealed := box.Seal(nil, plainData, &nonce, (*[32]byte)(thirdPartyPub), (*[32]byte)(&enc.key.Private)) return &caveatId{ ThirdPartyPublicKey: thirdPartyPub[:], FirstPartyPublicKey: enc.key.Public[:], Nonce: nonce[:], Id: base64.StdEncoding.EncodeToString(sealed), }, nil }
// send encrypts |message| and enqueues it for transmission. func (c *client) send(to *Contact, message *pond.Message) error { messageBytes, err := proto.Marshal(message) if err != nil { return err } if len(messageBytes) > pond.MaxSerializedMessage { return errors.New("message too large") } // All messages are padded to the maximum length. plaintext := make([]byte, pond.MaxSerializedMessage+4) binary.LittleEndian.PutUint32(plaintext, uint32(len(messageBytes))) copy(plaintext[4:], messageBytes) c.randBytes(plaintext[4+len(messageBytes):]) // The message is encrypted to an ephemeral key so that the sending // client can choose not to store it and then cannot decrypt it once // sent. // +---------------------+ +---... // outerNonce | ephemeral DH public | innerNonce | message // (24 bytes) | | (24 bytes) | // +---------------------+ +---.... sealedLen := ephemeralBlockLen + nonceLen + len(plaintext) + box.Overhead sealed := make([]byte, sealedLen) var outerNonce [24]byte c.randBytes(outerNonce[:]) copy(sealed, outerNonce[:]) x := sealed[nonceLen:] public, private, err := box.GenerateKey(c.rand) if err != nil { return err } box.Seal(x[:0], public[:], &outerNonce, &to.theirCurrentDHPublic, &to.lastDHPrivate) x = x[len(public)+box.Overhead:] var innerNonce [24]byte c.randBytes(innerNonce[:]) copy(x, innerNonce[:]) x = x[nonceLen:] box.Seal(x[:0], plaintext, &innerNonce, &to.theirCurrentDHPublic, private) sha := sha256.New() sha.Write(sealed) digest := sha.Sum(nil) sha.Reset() groupSig, err := to.myGroupKey.Sign(c.rand, digest, sha) if err != nil { return err } request := &pond.Request{ Deliver: &pond.Delivery{ To: to.theirIdentityPublic[:], Signature: groupSig, Generation: proto.Uint32(to.generation), Message: sealed, }, } out := &queuedMessage{ request: request, id: *message.Id, to: to.id, server: to.theirServer, message: message, created: time.Unix(*message.Time, 0), } c.enqueue(out) if len(message.Body) > 0 { c.outboxUI.Add(*message.Id, to.name, out.created.Format(shortTimeFormat), indicatorRed) } c.outbox = append(c.outbox, out) return nil }
func (s *server) pump() { rotateMinuteKey := time.NewTicker(30 * time.Second) for { select { case packet := <-s.packetIn: if s.checkHello(packet.buf) { resp := freelist.Packets.Get() resp, scratch := resp[:200], resp[200:] pkey, skey, err := box.GenerateKey(rand.Reader) if err != nil { panic("Ran out of randomness") } // Client short-term public key copy(scratch, packet.buf[40:40+32]) // Server short-term secret key copy(scratch[32:], skey[:]) // minute-key secretbox nonce var nonce [24]byte copy(nonce[:], minuteNoncePrefix) randBytes(nonce[len(minuteNoncePrefix):]) secretbox.Seal(scratch[:64], scratch[:64], &nonce, &s.minuteKey) // Compressed cookie nonce copy(scratch[48:64], nonce[len(minuteNoncePrefix):]) // Server short-term public key copy(scratch[16:48], pkey[:]) var clientKey [32]byte copy(clientKey[:], packet.buf[40:40+32]) // Cookie box nonce copy(nonce[:], cookieNoncePrefix) randBytes(nonce[len(cookieNoncePrefix):]) box.Seal(resp[:56], scratch[16:16+128], &nonce, &clientKey, &s.longTermSecretKey) // Packet header, with extensions swapped. copy(resp, cookieMagic) copy(resp[8:], packet.buf[24:24+16]) copy(resp[24:], packet.buf[8:8+16]) copy(resp[40:], nonce[8:]) s.sock.WriteTo(resp, packet.Addr) freelist.Packets.Put(resp) } else if serverShortTermKey, domain, valid := s.checkInitiate(packet.buf); valid { clientShortTermKey := packet.buf[40 : 40+32] clientLongTermKey := packet.buf[176 : 176+32] if ch, ok := s.conns[string(clientShortTermKey)]; ok { // Forward the Initiate to the conn. Because // checkInitiate replaces the box in the Initiate // packet with its plaintext, and because pump has // done all the crypto verification, conn can // ignore anything not relevant to maintaining // correct stream state. ch <- packet } else if s.listen { // This is a new client initiating. Construct a // conn and wait for someone to Accept() it. c := newConn(s.sock, clientLongTermKey, clientShortTermKey, serverShortTermKey, domain) // TODO: accept timeout or something. s.newConn <- c s.conns[string(clientShortTermKey)] = c.packetIn } } case <-s.stopListen: s.listen = false close(s.newConn) // We hang onto the long term secret key and minute keys // for one full minute key rotation, so that we can still // decode retransmitted Initiate packets for a while. The // rotateMinuteKey case below will take care of final // cleanup. case <-rotateMinuteKey.C: if !s.listen && bytes.Equal(s.minuteKey[:], s.prevMinuteKey[:]) { // At least 30 seconds have passed since we stopped // listening, we can clear the key material and stop // refreshing minute keys. for i := 0; i < len(s.longTermSecretKey); i++ { s.minuteKey[i] = 0 s.prevMinuteKey[i] = 0 s.longTermSecretKey[i] = 0 } rotateMinuteKey.Stop() } else { copy(s.prevMinuteKey[:], s.minuteKey[:]) if s.listen { randBytes(s.minuteKey[:]) } } } } }
func (c *client) send(to *Contact, message *pond.Message) error { messageBytes, err := proto.Marshal(message) if err != nil { return err } if len(messageBytes) > pond.MaxSerializedMessage { return errors.New("message too large") } plaintext := make([]byte, pond.MaxSerializedMessage+4) binary.LittleEndian.PutUint32(plaintext, uint32(len(messageBytes))) copy(plaintext[4:], messageBytes) c.randBytes(plaintext[4+len(messageBytes):]) var innerNonce [24]byte c.randBytes(innerNonce[:]) var sealed, innerSealed []byte sealedLen := nonceLen + len(plaintext) + box.Overhead dhPrivate := &to.lastDHPrivate if to.supportedVersion >= 1 { public, private, err := box.GenerateKey(c.rand) if err != nil { return err } dhPrivate = private var outerNonce [24]byte c.randBytes(outerNonce[:]) sealedLen += ephemeralBlockLen sealed = make([]byte, sealedLen) copy(sealed, outerNonce[:]) box.Seal(sealed[nonceLen:nonceLen], public[:], &outerNonce, &to.theirCurrentDHPublic, &to.lastDHPrivate) innerSealed = sealed[ephemeralBlockLen:] } else { sealed = make([]byte, sealedLen) innerSealed = sealed } copy(innerSealed, innerNonce[:]) box.Seal(innerSealed[nonceLen:nonceLen], plaintext, &innerNonce, &to.theirCurrentDHPublic, dhPrivate) sha := sha256.New() sha.Write(sealed) digest := sha.Sum(nil) sha.Reset() groupSig, err := to.myGroupKey.Sign(c.rand, digest, sha) if err != nil { return err } request := &pond.Request{ Deliver: &pond.Delivery{ To: to.theirIdentityPublic[:], Signature: groupSig, Generation: proto.Uint32(to.generation), Message: sealed, }, } out := &queuedMessage{ request: request, id: *message.Id, to: to.id, server: to.theirServer, message: message, created: time.Unix(*message.Time, 0), } c.enqueue(out) if len(message.Body) > 0 { c.outboxUI.Add(*message.Id, to.name, out.created.Format(shortTimeFormat), indicatorRed) } c.outbox = append(c.outbox, out) return nil }
// processSigningRequest is run on the main goroutine in response to a request // from the network thread to apply a group signature to a message that is just // about to be sent to the destination server. func (c *client) processSigningRequest(sigReq signingRequest) { defer close(sigReq.resultChan) to := c.contacts[sigReq.msg.to] messageBytes, err := proto.Marshal(sigReq.msg.message) if err != nil { c.log.Printf("Failed to sign outgoing message: %s", err) return } if len(messageBytes) > pond.MaxSerializedMessage { c.log.Printf("Failed to sign outgoing message because it's too large") return } // All messages are padded to the maximum length. plaintext := make([]byte, pond.MaxSerializedMessage+4) binary.LittleEndian.PutUint32(plaintext, uint32(len(messageBytes))) copy(plaintext[4:], messageBytes) c.randBytes(plaintext[4+len(messageBytes):]) var sealed []byte if to.ratchet != nil { sealed = to.ratchet.Encrypt(sealed, plaintext) } else { // The message is encrypted to an ephemeral key so that the sending // client can choose not to store it and then cannot decrypt it once // sent. // +---------------------+ +---... // outerNonce | ephemeral DH public | innerNonce | message // (24 bytes) | | (24 bytes) | // +---------------------+ +---.... sealedLen := ephemeralBlockLen + nonceLen + len(plaintext) + box.Overhead sealed = make([]byte, sealedLen) var outerNonce [24]byte c.randBytes(outerNonce[:]) copy(sealed, outerNonce[:]) x := sealed[nonceLen:] public, private, err := box.GenerateKey(c.rand) if err != nil { c.log.Printf("Failed to generate key for outgoing message: %s", err) return } box.Seal(x[:0], public[:], &outerNonce, &to.theirCurrentDHPublic, &to.lastDHPrivate) x = x[len(public)+box.Overhead:] var innerNonce [24]byte c.randBytes(innerNonce[:]) copy(x, innerNonce[:]) x = x[nonceLen:] box.Seal(x[:0], plaintext, &innerNonce, &to.theirCurrentDHPublic, private) } sha := sha256.New() sha.Write(sealed) digest := sha.Sum(nil) sha.Reset() groupSig, err := to.myGroupKey.Sign(c.rand, digest, sha) if err != nil { c.log.Printf("Failed to sign outgoing message: %s", err) return } request := &pond.Request{ Deliver: &pond.Delivery{ To: to.theirIdentityPublic[:], Signature: groupSig, Generation: proto.Uint32(to.generation), Message: sealed, }, } sigReq.resultChan <- request }
/* Encrypt & write message * Note that there is no way to directly convert a Message into CipherMessage struct, * but you can use this function to serialize one to any writer (e.g. file or network) */ func WriteCipherMessage(writer io.Writer, message *Message, from, to *Identity, permit string) error { newError := func(err error) error { return errors.New("Cannot encrypt message: " + err.Error()) } // need to fill: To, From, Hash, CipherKey, CipherChunkSize, Permit // generate a new symmetric key var key [32]byte _, err := rand.Read(key[:]) if err != nil { return newError(err) } // determine appropriate ChunkSize // for now, just use 10K bytes chunkSize := int64(10240) chunkSizeString := strconv.Itoa(int(chunkSize)) // calculate CipherText hash hasher := sha512.New() cipherWriter := NewCipherWriter(hasher, &key, chunkSize) err = message.Serialize(cipherWriter) if err != nil { return newError(err) } err = cipherWriter.Close() if err != nil { return newError(err) } cipherMessageSize := cipherWriter.written hashBytes := hasher.Sum([]byte{}) hashString := base64.URLEncoding.EncodeToString(hashBytes) // encrypt key to CipherKey var nonce [24]byte _, err = rand.Read(nonce[:]) if err != nil { return newError(err) } cipherKey := box.Seal(nil, key[:], &nonce, to.PublicKey, from.PrivateKey) cipherKey = append(nonce[:], cipherKey...) cipherKeyString := base64.URLEncoding.EncodeToString(cipherKey) // make header header := map[string]interface{}{ "To": KeyToString(to.PublicKey), "From": KeyToString(from.PublicKey), "Hash": hashString, "CipherKey": cipherKeyString, "CipherChunkSize": chunkSizeString, "Permit": permit, } headerBytes, err := json.Marshal(header) if err != nil { return newError(err) } // write! err = binary.Write(writer, binary.BigEndian, uint64(len(headerBytes))) if err != nil { return newError(err) } _, err = writer.Write(headerBytes) if err != nil { return newError(err) } err = binary.Write(writer, binary.BigEndian, uint64(cipherMessageSize)) if err != nil { return newError(err) } cipherWriter.Reset() cipherWriter.Writer = writer err = message.Serialize(cipherWriter) if err != nil { return newError(err) } err = cipherWriter.Close() if err != nil { return newError(err) } return nil }