tdo

Dicing the onion
git clone git@abtrout.com:tdo.git
Log | Files | Refs | README

decode.go (1889B)


      1 package main
      2 
      3 import (
      4 	"bytes"
      5 	"crypto/aes"
      6 	"crypto/cipher"
      7 	"encoding/binary"
      8 	"errors"
      9 	"log"
     10 	"os"
     11 
     12 	"github.com/abtrout/tdo"
     13 )
     14 
     15 func main() {
     16 	bs, err := tdo.DecodePipedInput()
     17 	if err != nil {
     18 		log.Fatalf("Failed to read layer input: %v", err)
     19 	}
     20 
     21 	// Split input based on instructions.
     22 	kek := bs[0:32]
     23 	keyIV, keyCT := bs[32:40], bs[40:80]
     24 	ctIV, ct := bs[80:96], bs[96:]
     25 	// Decrypt the key with kek and keyIV.
     26 	block, err := aes.NewCipher(kek)
     27 	if err != nil {
     28 		log.Fatalf("Failed to make NewCipher: %v", err)
     29 	}
     30 	key, err := KeyUnwrap(block, keyIV, keyCT)
     31 	if err != nil {
     32 		log.Fatalf("Failed to unwrap key: %v", err)
     33 	}
     34 	// Decrypt the ct with key and iv.
     35 	block, err = aes.NewCipher(key)
     36 	if err != nil {
     37 		log.Fatalf("Failed to make NewCipher: %v", err)
     38 	}
     39 	out := make([]byte, len(ct))
     40 	stream := cipher.NewCTR(block, ctIV)
     41 	stream.XORKeyStream(out, ct)
     42 
     43 	if _, err := os.Stdout.Write(out); err != nil {
     44 		log.Fatalf("Failed to write decoded output: %v", err)
     45 	}
     46 }
     47 
     48 // Key Unwrap is not implemented in the standard library.
     49 // https://datatracker.ietf.org/doc/html/rfc3394#section-2.2.2
     50 func KeyUnwrap(block cipher.Block, iv, ct []byte) ([]byte, error) {
     51 	// setup
     52 	a := make([]byte, 8)
     53 	copy(a, ct[0:8])
     54 	n := (len(ct) / 8) - 1 // n+1 blocks
     55 	r := make([][]byte, n)
     56 	for i := range r {
     57 		r[i] = ct[(i+1)*8 : (i+2)*8]
     58 	}
     59 	// unwrap
     60 	ctr := make([]byte, 8)
     61 	for j := 5; j >= 0; j-- {
     62 		for i := len(r) - 1; i >= 0; i-- {
     63 			binary.BigEndian.PutUint64(ctr, uint64(n*j+i+1))
     64 			// a = a ^ ctr
     65 			for i := range a {
     66 				a[i] ^= ctr[i]
     67 			}
     68 			// b = Decrypt(a || r[i])
     69 			b := append(a, r[i]...)
     70 			block.Decrypt(b, b)
     71 			// reset for next round
     72 			copy(a, b[0:8])
     73 			copy(r[i], b[8:16])
     74 		}
     75 	}
     76 	// verify
     77 	if !bytes.Equal(a, iv) {
     78 		return nil, errors.New("iv mismatch")
     79 	}
     80 	// output
     81 	var res []byte
     82 	for _, b := range r {
     83 		res = append(res, b...)
     84 	}
     85 	return res, nil
     86 }