// SessionAnchor returns the decrypted and verified session anchor for KeyInit. func (ki *KeyInit) SessionAnchor(sigPubKey string) (*SessionAnchor, error) { // SIGKEYHASH corresponds to the SIGKEY of the Identity pubKey, err := base64.Decode(sigPubKey) if err != nil { return nil, err } keyHash := cipher.SHA512(pubKey) if ki.Contents.SIGKEYHASH != base64.Encode(cipher.SHA512(keyHash)) { log.Error(ErrWrongSigKeyHash) return nil, ErrWrongSigKeyHash } // verify that SESSIONANCHORHASH matches decrypted SESSIONANCHOR enc, err := base64.Decode(ki.Contents.SESSIONANCHOR) if err != nil { return nil, err } txt := cipher.AES256CTRDecrypt(keyHash[:32], enc) var sa SessionAnchor if err := json.Unmarshal(txt, &sa); err != nil { return nil, log.Error(err) } if ki.Contents.SESSIONANCHORHASH != base64.Encode(cipher.SHA512(sa.json())) { log.Error(ErrSessionAnchor) return nil, ErrSessionAnchor } return &sa, nil }
// GetOutQueue returns the first entry in the outqueue for myID. // Entries which need to be resend are ignored. func (msgDB *MsgDB) GetOutQueue(myID string) ( oqIdx int64, msg, nymaddress string, minDelay, maxDelay int32, envelope bool, err error, ) { if err := identity.IsMapped(myID); err != nil { return 0, "", "", 0, 0, false, log.Error(err) } var mID int64 if err := msgDB.getNymUIDQuery.QueryRow(myID).Scan(&mID); err != nil { return 0, "", "", 0, 0, false, log.Error(err) } var e int64 err = msgDB.getOutQueueQuery.QueryRow(mID).Scan(&oqIdx, &msg, &nymaddress, &minDelay, &maxDelay, &e) switch { case err == sql.ErrNoRows: return 0, "", "", 0, 0, false, nil case err != nil: return 0, "", "", 0, 0, false, log.Error(err) } if e > 0 { envelope = true } return }
// ReadKeyfile reads a randomly generated and encrypted AES-256 key from the // file with the given filename and returns it in unencrypted form. // The key is protected by a passphrase, which is processed by PBKDF2 to // derive the AES-256 key to decrypt the generated key. func ReadKeyfile(filename string, passphrase []byte) (key []byte, err error) { // open keyfile keyfile, err := os.Open(filename) if err != nil { return nil, log.Error(err) } defer keyfile.Close() // read iter and convert to int var biter = make([]byte, 8) if _, err := keyfile.Read(biter); err != nil { return nil, log.Error(err) } uiter := encode.ToUint64(biter) if uiter > 2147483647 { return nil, log.Errorf("encdb: ReadKeyfile: invalid iter value") } iter := int(uiter) // read salt var salt = make([]byte, 32) if _, err := keyfile.Read(salt); err != nil { return nil, log.Error(err) } // read encrypted key var encKey = make([]byte, 16+32) if _, err := keyfile.Read(encKey); err != nil { return nil, log.Error(err) } // compute derived key from passphrase dk := pbkdf2.Key([]byte(passphrase), salt, iter, 32, sha256.New) // decrypt key return cipher.AES256CBCDecrypt([]byte(dk), encKey), nil }
// GetInQueue returns the first entry in the inqueue. func (msgDB *MsgDB) GetInQueue() ( iqIdx int64, myID, contactID, msg string, envelope bool, err error, ) { var mID int64 var cID int64 var env int64 err = msgDB.getInQueueQuery.QueryRow().Scan(&iqIdx, &mID, &cID, &msg, &env) switch { case err == sql.ErrNoRows: return 0, "", "", "", false, nil case err != nil: return 0, "", "", "", false, log.Error(err) } err = msgDB.getNymMappedQuery.QueryRow(mID).Scan(&myID) if err != nil { return 0, "", "", "", false, log.Error(err) } if cID > 0 { err = msgDB.getContactMappedQuery.QueryRow(mID, cID).Scan(&contactID) if err != nil { return 0, "", "", "", false, log.Error(err) } } if env > 0 { envelope = true } return }
// ECDH computes a Diffie-Hellman (DH) key exchange over the elliptic curve (EC) // curve25519. If ownPublicKey is given it is used to check for the key // reflection attack. Otherwise it is derived from privateKey. func ECDH(privateKey, peersPublicKey, ownPublicKey *[32]byte) (*[32]byte, error) { var ( sharedKey [32]byte pubKey []byte ) // check mandatory key length if privateKey == nil { return nil, log.Error("cipher: curve25519.ECDH(): privateKey == nil") } if peersPublicKey == nil { return nil, log.Error("cipher: curve25519.ECDH(): peersPublicKey == nil") } // check for key reflection attack if ownPublicKey != nil { pubKey = ownPublicKey[:] } else { var publicKey [32]byte curve25519.ScalarBaseMult(&publicKey, privateKey) pubKey = publicKey[:] } if bytes.Equal(pubKey, peersPublicKey[:]) { return nil, log.Errorf("cipher: curve25519.ECDH(): publicKey == peersPublicKey") } // perform Diffie-Hellman key exchange curve25519.ScalarMult(&sharedKey, privateKey, peersPublicKey) return &sharedKey, nil }
// GetAccountTime returns the account time for the given myID and contactID // combination. func (msgDB *MsgDB) GetAccountTime( myID, contactID string, ) (int64, error) { if err := identity.IsMapped(myID); err != nil { return 0, log.Error(err) } if contactID != "" { if err := identity.IsMapped(contactID); err != nil { return 0, log.Error(err) } } // get MyID var mID int if err := msgDB.getNymUIDQuery.QueryRow(myID).Scan(&mID); err != nil { return 0, log.Error(err) } // get ContactID var cID int if contactID != "" { err := msgDB.getContactUIDQuery.QueryRow(mID, contactID).Scan(&cID) if err != nil { return 0, log.Error(err) } } // get load time var loadTime int64 err := msgDB.getAccountTimeQuery.QueryRow(mID, cID).Scan(&loadTime) if err != nil { return 0, err } return loadTime, nil }
// RemoveOutQueue remove the message corresponding to oqIdx from the outqueue // and sets the send time of the corresponding message to date. func (msgDB *MsgDB) RemoveOutQueue(oqIdx, date int64) error { tx, err := msgDB.encDB.Begin() if err != nil { return log.Error(err) } var msgID int64 // get corresponding msgID err = tx.Stmt(msgDB.getOutQueueMsgIDQuery).QueryRow(oqIdx).Scan(&msgID) if err != nil { tx.Rollback() return log.Error(err) } // set date for message _, err = tx.Stmt(msgDB.updateMsgDateQuery).Exec(date, msgID) if err != nil { tx.Rollback() return log.Error(err) } // remove entry from outqueue if _, err := tx.Stmt(msgDB.removeOutQueueQuery).Exec(oqIdx); err != nil { tx.Rollback() return log.Error(err) } if err := tx.Commit(); err != nil { tx.Rollback() return log.Error(err) } return nil }
// Open tries to open an encrypted database with the given passphrase. // Thereby, dbname is the prefix of the following two database files (which // must already exist): // // dbname.db // dbname.key // // In case of error (for example, the database files do not exist or the // passphrase is wrong) an error is returned. func Open(dbname string, passphrase []byte) (*sql.DB, error) { dbfile := dbname + DBSuffix keyfile := dbname + KeySuffix // make sure files exists if _, err := os.Stat(dbfile); err != nil { return nil, log.Error(err) } if _, err := os.Stat(keyfile); err != nil { return nil, log.Error(err) } // get key from keyfile key, err := ReadKeyfile(keyfile, passphrase) if err != nil { return nil, err } // open DB dbfile += fmt.Sprintf("?_pragma_key=x'%s'&_pragma_cipher_page_size=4096", hex.EncodeToString(key)) // enable foreign key support dbfile += "&_pragma_foreign_keys=on" db, err := sql.Open("sqlite3", dbfile) if err != nil { return nil, log.Error(err) } // test key _, err = db.Exec("SELECT count(*) FROM sqlite_master;") if err != nil { return nil, log.Error(err) } return db, nil }
func readOuterHeader(r io.Reader) (*outerHeader, error) { var oh outerHeader // read Type if err := binary.Read(r, binary.BigEndian, &oh.Type); err != nil { return nil, log.Error(err) } // check Type if oh.Type != preHeaderPacket && oh.Type != encryptedHeader && oh.Type != cryptoSetup && oh.Type != hmacPacket && oh.Type != encryptedPacket { return nil, log.Errorf("msg: invalid outer header type %d", oh.Type) } // read Plen if err := binary.Read(r, binary.BigEndian, &oh.PLen); err != nil { return nil, log.Error(err) } // read PacketCount if err := binary.Read(r, binary.BigEndian, &oh.PacketCount); err != nil { return nil, log.Error(err) } // read inner packet oh.inner = make([]byte, oh.PLen) if _, err := io.ReadFull(r, oh.inner); err != nil { return nil, log.Error(err) } return &oh, nil }
// create a new KeyDB. func (ce *CryptEngine) dbCreate(homedir string, iterations int) error { keydbname := filepath.Join(homedir, "keys") // read passphrase log.Infof("read passphrase from fd %d", ce.fileTable.PassphraseFD) scanner := bufio.NewScanner(ce.fileTable.PassphraseFP) var passphrase []byte defer bzero.Bytes(passphrase) if scanner.Scan() { passphrase = scanner.Bytes() } else if err := scanner.Err(); err != nil { return log.Error(err) } // read passphrase again log.Infof("read passphrase from fd %d again", ce.fileTable.PassphraseFD) var passphrase2 []byte defer bzero.Bytes(passphrase2) if scanner.Scan() { passphrase2 = scanner.Bytes() } else if err := scanner.Err(); err != nil { return log.Error(err) } // compare passphrases if !bytes.Equal(passphrase, passphrase2) { return log.Error("passphrases differ") } // create keyDB log.Infof("create keyDB '%s'", keydbname) if err := keydb.Create(keydbname, passphrase, iterations); err != nil { return err } return nil }
// VerifyUserSig verifies that the user-signature of UIDMessage is valid. func (msg *Message) VerifyUserSig(preMsg *Message) error { var ed25519Key cipher.Ed25519Key // check message counter if preMsg.UIDContent.MSGCOUNT+1 != msg.UIDContent.MSGCOUNT { return log.Error(ErrIncrement) } // get content content := msg.UIDContent.JSON() // get self-signature selfsig, err := base64.Decode(msg.USERSIGNATURE) if err != nil { return err } // create ed25519 key pubKey, err := base64.Decode(preMsg.UIDContent.SIGKEY.PUBKEY) if err != nil { return err } if err := ed25519Key.SetPublicKey(pubKey); err != nil { return err } // verify self-signature if !ed25519Key.Verify(content, selfsig) { return log.Error(ErrInvalidUserSig) } return nil }
// deleteUID deletes a nym. func (ce *CryptEngine) deleteUID(pseudonym string, force bool) error { // map pseudonym id, err := identity.Map(pseudonym) if err != nil { return err } // get UID from keyDB msg, _, err := ce.keyDB.GetPrivateUID(id, false) if err != nil { return err } // ask for manual confirmation if !force { fmt.Fprintf(os.Stderr, "cryptengine: delete user ID %s and all it's key material? ", pseudonym) var response string _, err := fmt.Scanln(&response) if err != nil { return log.Error(err) } if !strings.HasPrefix(strings.ToLower(response), "y") { return log.Error("cryptengine: user ID deletion aborted") } } // delete UID from keyDB if err := ce.keyDB.DelPrivateUID(msg); err != nil { return err } return nil }
// Create tries to create an encrypted database with the given passphrase and // iter many KDF iterations. Thereby, dbname is the prefix of the following // two database files which will be created and must not exist already: // // dbname.db // dbname.key // // The SQL database is initialized with the statements given in createStmts. // In case of error (for example, the database files do exist already or // cannot be created) an error is returned. func Create(dbname string, passphrase []byte, iter int, createStmts []string) error { dbfile := dbname + DBSuffix keyfile := dbname + KeySuffix // make sure files do not exist already if _, err := os.Stat(dbfile); err == nil { return log.Errorf("encdb: dbfile '%s' exists already", dbfile) } if _, err := os.Stat(keyfile); err == nil { return log.Errorf("encdb: dbfile '%s' exists already", keyfile) } // create keyfile key, err := generateKeyfile(keyfile, passphrase, iter) if err != nil { return err } // create DB dbfile += fmt.Sprintf("?_pragma_key=x'%s'&_pragma_cipher_page_size=4096", hex.EncodeToString(key)) db, err := sql.Open("sqlite3", dbfile) if err != nil { return log.Error(err) } defer db.Close() _, err = db.Exec("PRAGMA auto_vacuum = full;") if err != nil { return log.Error(err) } return createTables(db, createStmts) }
func readInnerHeader(r io.Reader) (*innerHeader, error) { var ih innerHeader // read Type if err := binary.Read(r, binary.BigEndian, &ih.Type); err != nil { return nil, err } // check Type if ih.Type != paddingType && ih.Type != dataType && ih.Type != dataType|signType && ih.Type != signatureType { return nil, log.Errorf("msg: invalid inner header type %d", ih.Type) } // read Plen if err := binary.Read(r, binary.BigEndian, &ih.PLen); err != nil { return nil, log.Error(err) } // read More if err := binary.Read(r, binary.BigEndian, &ih.More); err != nil { return nil, log.Error(err) } // read Skip if err := binary.Read(r, binary.BigEndian, &ih.Skip); err != nil { return nil, log.Error(err) } // read content ih.content = make([]byte, ih.PLen) if _, err := io.ReadFull(r, ih.content); err != nil { return nil, log.Error(err) } return &ih, nil }
// SetAccountLastMsg sets the last message time and ID for the given myID and // contactID combination (contactID can be nil). func (msgDB *MsgDB) SetAccountLastMsg( myID, contactID string, lastMessageTime int64, ) error { if err := identity.IsMapped(myID); err != nil { return log.Error(err) } if contactID != "" { if err := identity.IsMapped(contactID); err != nil { return log.Error(err) } } // get MyID var mID int if err := msgDB.getNymUIDQuery.QueryRow(myID).Scan(&mID); err != nil { return log.Error(err) } // get ContactID var cID int if contactID != "" { err := msgDB.getContactUIDQuery.QueryRow(mID, contactID).Scan(&cID) if err != nil { return log.Error(err) } } // set account time _, err := msgDB.setAccountLastTimeQuery.Exec(lastMessageTime, mID, cID) if err != nil { return log.Error(err) } return nil }
// GetNyms returns all unmapped or mapped nyms in msgDB. func (msgDB *MsgDB) GetNyms(mapped bool) ([]string, error) { // get contacts rows, err := msgDB.getNymsQuery.Query() if err != nil { return nil, log.Error(err) } var nyms []string defer rows.Close() for rows.Next() { var mappedID, unmappedID, fullName string if err := rows.Scan(&mappedID, &unmappedID, &fullName); err != nil { return nil, log.Error(err) } if mapped { nyms = append(nyms, mappedID) } else { if fullName == "" { nyms = append(nyms, unmappedID) } else { nyms = append(nyms, fullName+" <"+unmappedID+">") } } } if err := rows.Err(); err != nil { return nil, log.Error(err) } return nyms, nil }
// GetUndeliveredMessage returns the oldest undelivered message for myID from // msgDB. func (msgDB *MsgDB) GetUndeliveredMessage(myID string) ( msgNum int64, contactID string, msg []byte, sign bool, minDelay, maxDelay int32, err error, ) { if err := identity.IsMapped(myID); err != nil { return 0, "", nil, false, 0, 0, log.Error(err) } var mID int64 if err := msgDB.getNymUIDQuery.QueryRow(myID).Scan(&mID); err != nil { return 0, "", nil, false, 0, 0, log.Error(err) } var cID int64 var s int64 err = msgDB.getUndeliveredMsgQuery.QueryRow(mID).Scan(&msgNum, &cID, &msg, &s, &minDelay, &maxDelay) switch { case err == sql.ErrNoRows: return 0, "", nil, false, 0, 0, nil case err != nil: return 0, "", nil, false, 0, 0, log.Error(err) } if s > 0 { sign = true } err = msgDB.getContactMappedQuery.QueryRow(mID, cID).Scan(&contactID) if err != nil { return 0, "", nil, false, 0, 0, log.Error(err) } return }
// GetMsgIDs returns all message IDs (sqlite row IDs) for the user ID myID. func (msgDB *MsgDB) GetMsgIDs(myID string) ([]*MsgID, error) { if err := identity.IsMapped(myID); err != nil { return nil, log.Error(err) } var uid int if err := msgDB.getNymUIDQuery.QueryRow(myID).Scan(&uid); err != nil { return nil, log.Error(err) } rows, err := msgDB.getMsgsQuery.Query(uid) if err != nil { return nil, log.Error(err) } var msgIDs []*MsgID defer rows.Close() for rows.Next() { var ( id int64 from string to string d int64 s int64 date int64 subject string r int64 ) err = rows.Scan(&id, &from, &to, &d, &s, &date, &subject, &r) if err != nil { return nil, log.Error(err) } var ( incoming bool sent bool read bool ) if d == 0 { incoming = true } if s > 0 { sent = true } if r > 0 { read = true } msgIDs = append(msgIDs, &MsgID{ MsgID: id, From: from, To: to, Incoming: incoming, Sent: sent, Date: date, Subject: subject, Read: read, }) } if err := rows.Err(); err != nil { return nil, log.Error(err) } return msgIDs, nil }
// SetUpkeepAccounts sets the last execution time of 'upkeep accounts' to t. func (msgDB *MsgDB) SetUpkeepAccounts(myID string, t int64) error { if err := identity.IsMapped(myID); err != nil { return log.Error(err) } if _, err := msgDB.setUpkeepAccountsQuery.Exec(t, myID); err != nil { return log.Error(err) } return nil }
// DelPrivSessionKey deletes the private key corresponding to the session key // with given hash from keyDB. func (keyDB *KeyDB) DelPrivSessionKey(hash string) error { if hash == "" { return log.Error("keydb: hash must be defined") } _, err := keyDB.updateSessionKeyQuery.Exec("", hash) if err != nil { return log.Error(err) } return nil }
// GetUpkeepAccounts retrieves the last execution time of 'upkeep accounts'. func (msgDB *MsgDB) GetUpkeepAccounts(myID string) (int64, error) { if err := identity.IsMapped(myID); err != nil { return 0, log.Error(err) } var t int64 if err := msgDB.getUpkeepAccountsQuery.QueryRow(myID).Scan(&t); err != nil { return 0, log.Error(err) } return t, nil }
// KeyInit returns a new KeyInit message for the given UID message. It also // returns the pubKeyHash and privateKey for convenient further use. // msgcount must increase for each message of the same type and user. // notafter is the unixtime after which the key(s) should not be used anymore. // notbefore is the unixtime before which the key(s) should not be used yet. // fallback determines if the key may serve as a fallback key. // repoURI is URI of the corresponding KeyInit repository. // Necessary randomness is read from rand. func (msg *Message) KeyInit( msgcount, notafter, notbefore uint64, fallback bool, repoURI, mixaddress, nymaddress string, rand io.Reader, ) (ki *KeyInit, pubKeyHash, privateKey string, err error) { var keyInit KeyInit // time checks if notbefore >= notafter { log.Error(ErrInvalidTimes) return nil, "", "", ErrInvalidTimes } if notafter < uint64(times.Now()) { log.Error(ErrExpired) return nil, "", "", ErrExpired } if notafter > uint64(times.Now())+MaxNotAfter { log.Error(ErrFuture) return nil, "", "", ErrFuture } // init keyInit.Contents.VERSION = ProtocolVersion keyInit.Contents.MSGCOUNT = msgcount keyInit.Contents.NOTAFTER = notafter keyInit.Contents.NOTBEFORE = notbefore keyInit.Contents.FALLBACK = fallback keyHash, err := base64.Decode(msg.UIDContent.SIGKEY.HASH) if err != nil { return nil, "", "", err } keyInit.Contents.SIGKEYHASH = base64.Encode(cipher.SHA512(keyHash)) // make sure REPOURIS is set to the first REPOURI of UIDContent.REPOURIS // TODO: support different KeyInit repository configurations if repoURI != msg.UIDContent.REPOURIS[0] { return nil, "", "", log.Error("uri: repoURI differs from msg.UIDContent.REPOURIS[0]") } keyInit.Contents.REPOURI = repoURI // create SessionAnchor sa, sah, pubKeyHash, privateKey, err := msg.sessionAnchor(keyHash, mixaddress, nymaddress, rand) if err != nil { return nil, "", "", err } keyInit.Contents.SESSIONANCHOR = sa keyInit.Contents.SESSIONANCHORHASH = sah // sign KeyInit: the content doesn't have to be hashed, because Ed25519 is // already taking care of that. sig := msg.UIDContent.SIGKEY.ed25519Key.Sign(keyInit.Contents.json()) keyInit.SIGNATURE = base64.Encode(sig) ki = &keyInit return }
// Verify verifies that the KeyInit is valid and contains a valid ECDHE25519 // key. func (ki *KeyInit) Verify(keyInitRepositoryURIs []string, sigPubKey string) error { // The REPOURI points to this KeyInit Repository if !util.ContainsString(keyInitRepositoryURIs, ki.Contents.REPOURI) { log.Error(ErrRepoURI) return ErrRepoURI } // verify that SESSIONANCHORHASH matches decrypted SESSIONANCHOR sa, err := ki.SessionAnchor(sigPubKey) if err != nil { return err } // get KeyEntry message from SessionAnchor ke, err := sa.KeyEntry("ECDHE25519") if err != nil { return err } // verify KeyEntry message if err := ke.Verify(); err != nil { return err } // NOTAFTER and NOTBEFORE are valid if ki.Contents.NOTBEFORE >= ki.Contents.NOTAFTER { log.Error(ErrInvalidTimes) return ErrInvalidTimes } // not expired if ki.Contents.NOTAFTER < uint64(times.Now()) { log.Error(ErrExpired) return ErrExpired } // SIGNATURE was made with UIDMessage.UIDContent.SIGKEY over Contents var ed25519Key cipher.Ed25519Key sig, err := base64.Decode(ki.SIGNATURE) if err != nil { return err } pubKey, err := base64.Decode(sigPubKey) if err != nil { return err } // create ed25519 key ed25519Key.SetPublicKey(pubKey) // verify self-signature if !ed25519Key.Verify(ki.Contents.json(), sig) { log.Error(ErrInvalidKeyInitSig) return ErrInvalidKeyInitSig } return nil }
func writeConfigFile(homedir, domain string, config []byte) error { configdir := filepath.Join(homedir, "config") if err := os.MkdirAll(configdir, 0700); err != nil { return log.Error(err) } tmpfile := filepath.Join(configdir, domain+".new") os.Remove(tmpfile) // ignore error if err := ioutil.WriteFile(tmpfile, config, 0600); err != nil { return log.Error(err) } return os.Rename(tmpfile, filepath.Join(configdir, domain)) }
// AddMessage adds message between selfID and peerID to msgDB. If sent is // true, it is a sent message. Otherwise a received message. func (msgDB *MsgDB) AddMessage( selfID, peerID string, date int64, sent bool, message string, sign bool, minDelay, maxDelay int32, ) error { if err := identity.IsMapped(selfID); err != nil { return log.Error(err) } if err := identity.IsMapped(peerID); err != nil { return log.Error(err) } // get self var self int64 if err := msgDB.getNymUIDQuery.QueryRow(selfID).Scan(&self); err != nil { return log.Error(err) } // get peer var peer int64 err := msgDB.getContactUIDQuery.QueryRow(self, peerID).Scan(&peer) if err != nil { return log.Error(err) } // add message var d int64 if sent { d = 1 } var s int64 if sign { s = 1 } var from string var to string if sent { from = selfID to = peerID } else { from = peerID to = selfID } parts := strings.SplitN(message, "\n", 2) subject := parts[0] _, err = msgDB.addMsgQuery.Exec(self, peer, d, d, 0, from, to, date, subject, message, s, minDelay, maxDelay) if err != nil { return log.Error(err) } return nil }
// ClearResendOutQueue clears the resend status in the outqueue for myID. func (msgDB *MsgDB) ClearResendOutQueue(myID string) error { if err := identity.IsMapped(myID); err != nil { return log.Error(err) } var mID int64 if err := msgDB.getNymUIDQuery.QueryRow(myID).Scan(&mID); err != nil { return log.Error(err) } if _, err := msgDB.clearResendOutQueueQuery.Exec(mID); err != nil { return log.Error(err) } return nil }
func multipartMIME( writer *multipart.Writer, msg string, attachments []*Attachment, ) error { // write message mh := make(textproto.MIMEHeader) mh.Add("Content-Type", "text/plain") mh.Add("Content-Transfer-Encoding", "base64") msgWriter, err := writer.CreatePart(mh) if err != nil { return log.Error(err) } _, err = io.WriteString(msgWriter, base64.Encode([]byte(msg))) if err != nil { return log.Error(err) } // write attachments for _, attachment := range attachments { mh = make(textproto.MIMEHeader) base := filepath.Base(attachment.Filename) if attachment.ContentType != "" { mh.Add("Content-Type", attachment.ContentType) } else { ct := mime.TypeByExtension(filepath.Ext(base)) if ct != "" { mh.Add("Content-Type", ct) } else { mh.Add("Content-Type", "application/octet-stream") } } mh.Add("Content-Transfer-Encoding", "base64") mh.Add("Content-Disposition", "attachment; filename="+base) if attachment.Inline { mh.Add("Content-Disposition", "inline") } attachmentWriter, err := writer.CreatePart(mh) if err != nil { return log.Error(err) } encoder := base64.NewEncoder(attachmentWriter) if _, err := io.Copy(encoder, attachment.Reader); err != nil { return log.Error(err) } if err := encoder.Close(); err != nil { return log.Error(err) } } return nil }
func (hp *headerPacket) write(w io.Writer) error { //log.Debugf("hp.Nonce: %s", base64.Encode(hp.Nonce[:])) if _, err := w.Write(hp.Nonce[:]); err != nil { return log.Error(err) } //log.Debugf("hp.LengthEncryptedHeader: %d", hp.LengthEncryptedHeader) if err := binary.Write(w, binary.BigEndian, hp.LengthEncryptedHeader); err != nil { return log.Error(err) } if _, err := w.Write(hp.EncryptedHeader); err != nil { return log.Error(err) } return nil }
func readHeader( senderHeaderPub *[32]byte, identities []*uid.Message, r io.Reader, ) (*uid.Message, *header, error) { var hp headerPacket // read nonce if _, err := io.ReadFull(r, hp.Nonce[:]); err != nil { return nil, nil, log.Error(err) } //log.Debugf("hp.Nonce: %s", base64.Encode(hp.Nonce[:])) // read length of encrypted header if err := binary.Read(r, binary.BigEndian, &hp.LengthEncryptedHeader); err != nil { return nil, nil, log.Error(err) } //log.Debugf("hp.LengthEncryptedHeader: %d", hp.LengthEncryptedHeader) // read encrypted header hp.EncryptedHeader = make([]byte, hp.LengthEncryptedHeader) if _, err := io.ReadFull(r, hp.EncryptedHeader); err != nil { return nil, nil, log.Error(err) } // try to decrypt header var jsn []byte var suc bool var identity *uid.Message for _, uidMsg := range identities { log.Debugf("try identity %s (#%d)", uidMsg.Identity(), uidMsg.UIDContent.MSGCOUNT) log.Debugf("recvPub=%s\n", base64.Encode(uidMsg.PubKey().PublicKey32()[:])) jsn, suc = box.Open(jsn, hp.EncryptedHeader, &hp.Nonce, senderHeaderPub, uidMsg.PubKey().PrivateKey32()) if suc { identity = uidMsg break } } if !suc { return nil, nil, log.Error(ErrNoPreHeaderKey) } var h header if err := json.Unmarshal(jsn, &h); err != nil { return nil, nil, err } // verify header if err := h.verify(); err != nil { return nil, nil, err } return identity, &h, nil }
// Decrypt decrypts the message reply and returns the resulting UIDIndex and // UIDMesssage. func (reply *MessageReply) Decrypt(UIDHash []byte) ([]byte, *Message, error) { UIDMessageEncrypted, err := base64.Decode(reply.ENTRY.UIDMESSAGEENCRYPTED) if err != nil { return nil, nil, log.Error(err) } UIDIndex := UIDMessageEncrypted[:sha256.Size] enc := UIDMessageEncrypted[sha256.Size:] Message := cipher.AES256CTRDecrypt(UIDHash, enc) uid, err := NewJSON(string(Message)) if err != nil { return nil, nil, log.Error(err) } return UIDIndex, uid, nil }