| // Copyright 2016 The BoringSSL Authors | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     https://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | package runner | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"crypto/aes" | 
 | 	"crypto/cipher" | 
 | 	"crypto/hmac" | 
 | 	"crypto/sha256" | 
 | 	"encoding/asn1" | 
 | 	"errors" | 
 | ) | 
 |  | 
 | // TestShimTicketKey is the testing key assumed for the shim. | 
 | var TestShimTicketKey = make([]byte, 48) | 
 |  | 
 | func DecryptShimTicket(in []byte) ([]byte, error) { | 
 | 	name := TestShimTicketKey[:16] | 
 | 	macKey := TestShimTicketKey[16:32] | 
 | 	encKey := TestShimTicketKey[32:48] | 
 |  | 
 | 	h := hmac.New(sha256.New, macKey) | 
 |  | 
 | 	block, err := aes.NewCipher(encKey) | 
 | 	if err != nil { | 
 | 		panic(err) | 
 | 	} | 
 |  | 
 | 	if len(in) < len(name)+block.BlockSize()+1+h.Size() { | 
 | 		return nil, errors.New("tls: shim ticket too short") | 
 | 	} | 
 |  | 
 | 	// Check the key name. | 
 | 	if !bytes.Equal(name, in[:len(name)]) { | 
 | 		return nil, errors.New("tls: shim ticket name mismatch") | 
 | 	} | 
 |  | 
 | 	// Check the MAC at the end of the ticket. | 
 | 	mac := in[len(in)-h.Size():] | 
 | 	in = in[:len(in)-h.Size()] | 
 | 	h.Write(in) | 
 | 	if !hmac.Equal(mac, h.Sum(nil)) { | 
 | 		return nil, errors.New("tls: shim ticket MAC mismatch") | 
 | 	} | 
 |  | 
 | 	// The MAC covers the key name, but the encryption does not. | 
 | 	in = in[len(name):] | 
 |  | 
 | 	// Decrypt in-place. | 
 | 	iv := in[:block.BlockSize()] | 
 | 	in = in[block.BlockSize():] | 
 | 	if l := len(in); l == 0 || l%block.BlockSize() != 0 { | 
 | 		return nil, errors.New("tls: ticket ciphertext not a multiple of the block size") | 
 | 	} | 
 | 	out := make([]byte, len(in)) | 
 | 	cbc := cipher.NewCBCDecrypter(block, iv) | 
 | 	cbc.CryptBlocks(out, in) | 
 |  | 
 | 	// Remove the padding. | 
 | 	pad := int(out[len(out)-1]) | 
 | 	if pad == 0 || pad > block.BlockSize() || pad > len(in) { | 
 | 		return nil, errors.New("tls: bad shim ticket CBC pad") | 
 | 	} | 
 |  | 
 | 	for i := 0; i < pad; i++ { | 
 | 		if out[len(out)-1-i] != byte(pad) { | 
 | 			return nil, errors.New("tls: bad shim ticket CBC pad") | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return out[:len(out)-pad], nil | 
 | } | 
 |  | 
 | func EncryptShimTicket(in []byte) []byte { | 
 | 	name := TestShimTicketKey[:16] | 
 | 	macKey := TestShimTicketKey[16:32] | 
 | 	encKey := TestShimTicketKey[32:48] | 
 |  | 
 | 	h := hmac.New(sha256.New, macKey) | 
 |  | 
 | 	block, err := aes.NewCipher(encKey) | 
 | 	if err != nil { | 
 | 		panic(err) | 
 | 	} | 
 |  | 
 | 	// Use the zero IV for rewritten tickets. | 
 | 	iv := make([]byte, block.BlockSize()) | 
 | 	cbc := cipher.NewCBCEncrypter(block, iv) | 
 | 	pad := block.BlockSize() - (len(in) % block.BlockSize()) | 
 |  | 
 | 	out := make([]byte, 0, len(name)+len(iv)+len(in)+pad+h.Size()) | 
 | 	out = append(out, name...) | 
 | 	out = append(out, iv...) | 
 | 	out = append(out, in...) | 
 | 	for i := 0; i < pad; i++ { | 
 | 		out = append(out, byte(pad)) | 
 | 	} | 
 |  | 
 | 	ciphertext := out[len(name)+len(iv):] | 
 | 	cbc.CryptBlocks(ciphertext, ciphertext) | 
 |  | 
 | 	h.Write(out) | 
 | 	return h.Sum(out) | 
 | } | 
 |  | 
 | const asn1Constructed = 0x20 | 
 |  | 
 | func parseDERElement(in []byte) (tag byte, body, rest []byte, ok bool) { | 
 | 	rest = in | 
 | 	if len(rest) < 1 { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	tag = rest[0] | 
 | 	rest = rest[1:] | 
 |  | 
 | 	if tag&0x1f == 0x1f { | 
 | 		// Long-form tags not supported. | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if len(rest) < 1 { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	length := int(rest[0]) | 
 | 	rest = rest[1:] | 
 | 	if length > 0x7f { | 
 | 		lengthLength := length & 0x7f | 
 | 		length = 0 | 
 | 		if lengthLength == 0 { | 
 | 			// No indefinite-length encoding. | 
 | 			return | 
 | 		} | 
 |  | 
 | 		// Decode long-form lengths. | 
 | 		for lengthLength > 0 { | 
 | 			if len(rest) < 1 || (length<<8)>>8 != length { | 
 | 				return | 
 | 			} | 
 | 			if length == 0 && rest[0] == 0 { | 
 | 				// Length not minimally-encoded. | 
 | 				return | 
 | 			} | 
 | 			length <<= 8 | 
 | 			length |= int(rest[0]) | 
 | 			rest = rest[1:] | 
 | 			lengthLength-- | 
 | 		} | 
 |  | 
 | 		if length < 0x80 { | 
 | 			// Length not minimally-encoded. | 
 | 			return | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if len(rest) < length { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	body = rest[:length] | 
 | 	rest = rest[length:] | 
 | 	ok = true | 
 | 	return | 
 | } | 
 |  | 
 | func SetShimTicketVersion(in []byte, vers uint16) ([]byte, error) { | 
 | 	plaintext, err := DecryptShimTicket(in) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 |  | 
 | 	tag, session, _, ok := parseDERElement(plaintext) | 
 | 	if !ok || tag != asn1.TagSequence|asn1Constructed { | 
 | 		return nil, errors.New("tls: could not decode shim session") | 
 | 	} | 
 |  | 
 | 	// Skip the session version. | 
 | 	tag, _, session, ok = parseDERElement(session) | 
 | 	if !ok || tag != asn1.TagInteger { | 
 | 		return nil, errors.New("tls: could not decode shim session") | 
 | 	} | 
 |  | 
 | 	// Next field is the protocol version. | 
 | 	tag, version, _, ok := parseDERElement(session) | 
 | 	if !ok || tag != asn1.TagInteger { | 
 | 		return nil, errors.New("tls: could not decode shim session") | 
 | 	} | 
 |  | 
 | 	// This code assumes both old and new versions are encoded in two | 
 | 	// bytes. This isn't quite right as INTEGERs are minimally-encoded, but | 
 | 	// we do not need to support other caess for now. | 
 | 	if len(version) != 2 || vers < 0x80 || vers >= 0x8000 { | 
 | 		return nil, errors.New("tls: unsupported version in shim session") | 
 | 	} | 
 |  | 
 | 	version[0] = byte(vers >> 8) | 
 | 	version[1] = byte(vers) | 
 |  | 
 | 	return EncryptShimTicket(plaintext), nil | 
 | } | 
 |  | 
 | func SetShimTicketCipherSuite(in []byte, id uint16) ([]byte, error) { | 
 | 	plaintext, err := DecryptShimTicket(in) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 |  | 
 | 	tag, session, _, ok := parseDERElement(plaintext) | 
 | 	if !ok || tag != asn1.TagSequence|asn1Constructed { | 
 | 		return nil, errors.New("tls: could not decode shim session") | 
 | 	} | 
 |  | 
 | 	// Skip the session version. | 
 | 	tag, _, session, ok = parseDERElement(session) | 
 | 	if !ok || tag != asn1.TagInteger { | 
 | 		return nil, errors.New("tls: could not decode shim session") | 
 | 	} | 
 |  | 
 | 	// Skip the protocol version. | 
 | 	tag, _, session, ok = parseDERElement(session) | 
 | 	if !ok || tag != asn1.TagInteger { | 
 | 		return nil, errors.New("tls: could not decode shim session") | 
 | 	} | 
 |  | 
 | 	// Next field is the cipher suite. | 
 | 	tag, cipherSuite, _, ok := parseDERElement(session) | 
 | 	if !ok || tag != asn1.TagOctetString || len(cipherSuite) != 2 { | 
 | 		return nil, errors.New("tls: could not decode shim session") | 
 | 	} | 
 |  | 
 | 	cipherSuite[0] = byte(id >> 8) | 
 | 	cipherSuite[1] = byte(id) | 
 |  | 
 | 	return EncryptShimTicket(plaintext), nil | 
 | } |