462 lines
12 KiB
Go
462 lines
12 KiB
Go
package commands
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/binary"
|
||
"errors"
|
||
"io"
|
||
|
||
"github.com/certusone/yubihsm-go/authkey"
|
||
)
|
||
|
||
|
||
func CreateDeviceInfoCommand() (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeDeviceInfo,
|
||
}
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateCreateSessionCommand(keySetID uint16, hostChallenge []byte) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeCreateSession,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, keySetID)
|
||
payload.Write(hostChallenge)
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateAuthenticateSessionCommand(hostCryptogram []byte) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeAuthenticateSession,
|
||
Data: hostCryptogram,
|
||
}
|
||
|
||
return command, nil
|
||
}
|
||
|
||
// Authenticated
|
||
|
||
func CreateResetCommand() (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeReset,
|
||
}
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateGenerateAsymmetricKeyCommand(keyID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm) (*CommandMessage, error) {
|
||
if len(label) > LabelLength {
|
||
return nil, errors.New("label is too long")
|
||
}
|
||
if len(label) < LabelLength {
|
||
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
|
||
}
|
||
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeGenerateAsymmetricKey,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, keyID)
|
||
payload.Write(label)
|
||
binary.Write(payload, binary.BigEndian, domains)
|
||
binary.Write(payload, binary.BigEndian, capabilities)
|
||
binary.Write(payload, binary.BigEndian, algorithm)
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateSignDataEddsaCommand(keyID uint16, data []byte) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeSignDataEddsa,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, keyID)
|
||
payload.Write(data)
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateSignDataEcdsaCommand(keyID uint16, data []byte) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeSignDataEcdsa,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, keyID)
|
||
payload.Write(data)
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateSignDataPkcs1Command(keyID uint16, data []byte) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeSignDataPkcs1,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, keyID)
|
||
payload.Write(data)
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreatePutAsymmetricKeyCommand(keyID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, keyPart1 []byte, keyPart2 []byte) (*CommandMessage, error) {
|
||
if len(label) > LabelLength {
|
||
return nil, errors.New("label is too long")
|
||
}
|
||
if len(label) < LabelLength {
|
||
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
|
||
}
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypePutAsymmetric,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, keyID)
|
||
payload.Write(label)
|
||
binary.Write(payload, binary.BigEndian, domains)
|
||
binary.Write(payload, binary.BigEndian, capabilities)
|
||
binary.Write(payload, binary.BigEndian, algorithm)
|
||
payload.Write(keyPart1)
|
||
if keyPart2 != nil {
|
||
payload.Write(keyPart2)
|
||
}
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
type ListCommandOption func(w io.Writer)
|
||
|
||
func NewObjectTypeOption(objectType uint8) ListCommandOption {
|
||
return func(w io.Writer) {
|
||
binary.Write(w, binary.BigEndian, ListObjectParamType)
|
||
binary.Write(w, binary.BigEndian, objectType)
|
||
}
|
||
}
|
||
|
||
func NewIDOption(id uint16) ListCommandOption {
|
||
return func(w io.Writer) {
|
||
binary.Write(w, binary.BigEndian, ListObjectParamID)
|
||
binary.Write(w, binary.BigEndian, id)
|
||
}
|
||
}
|
||
|
||
func NewDomainOption(domain uint16) ListCommandOption {
|
||
return func(w io.Writer) {
|
||
binary.Write(w, binary.BigEndian, ListObjectParamDomains)
|
||
binary.Write(w, binary.BigEndian, domain)
|
||
}
|
||
}
|
||
|
||
func NewLabelOption(label []byte) (ListCommandOption, error) {
|
||
if len(label) > LabelLength {
|
||
return nil, errors.New("label is too long")
|
||
}
|
||
if len(label) < LabelLength {
|
||
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
|
||
}
|
||
return func(w io.Writer) {
|
||
binary.Write(w, binary.BigEndian, ListObjectParamLabel)
|
||
binary.Write(w, binary.BigEndian, label)
|
||
}, nil
|
||
}
|
||
|
||
func CreateListObjectsCommand(options ...ListCommandOption) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeListObjects,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
for _, opt := range options {
|
||
opt(payload)
|
||
}
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateGetObjectInfoCommand(keyID uint16, objectType uint8) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeGetObjectInfo,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, keyID)
|
||
binary.Write(payload, binary.BigEndian, objectType)
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateCloseSessionCommand() (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeCloseSession,
|
||
}
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateGetPubKeyCommand(keyID uint16) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeGetPubKey,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, keyID)
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateDeleteObjectCommand(objID uint16, objType uint8) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeDeleteObject,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, objID)
|
||
binary.Write(payload, binary.BigEndian, objType)
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateEchoCommand(data []byte) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeEcho,
|
||
Data: data,
|
||
}
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateDeriveEcdhCommand(objID uint16, pubkey []byte) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeDeriveEcdh,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, objID)
|
||
payload.Write(pubkey)
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateChangeAuthenticationKeyCommand(objID uint16, newPassword string) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeChangeAuthenticationKey,
|
||
}
|
||
|
||
authKey := authkey.NewFromPassword(newPassword)
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, objID)
|
||
binary.Write(payload, binary.BigEndian, AlgorithmYubicoAESAuthentication)
|
||
payload.Write(authKey.GetEncKey())
|
||
payload.Write(authKey.GetMacKey())
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreatePutOpaqueCommand(objID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, data []byte) (*CommandMessage, error) {
|
||
if len(label) > LabelLength {
|
||
return nil, errors.New("label is too long")
|
||
}
|
||
if len(label) < LabelLength {
|
||
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
|
||
}
|
||
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypePutOpaque,
|
||
}
|
||
|
||
payload := bytes.NewBuffer(nil)
|
||
binary.Write(payload, binary.BigEndian, objID)
|
||
payload.Write(label)
|
||
binary.Write(payload, binary.BigEndian, domains)
|
||
binary.Write(payload, binary.BigEndian, capabilities)
|
||
binary.Write(payload, binary.BigEndian, algorithm)
|
||
payload.Write(data)
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateGetOpaqueCommand(objID uint16) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeGetOpaque,
|
||
}
|
||
|
||
payload := bytes.NewBuffer(nil)
|
||
binary.Write(payload, binary.BigEndian, objID)
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateGetPseudoRandomCommand(numBytes uint16) *CommandMessage {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeGetPseudoRandom,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, numBytes)
|
||
command.Data = payload.Bytes()
|
||
|
||
return command
|
||
}
|
||
|
||
func CreatePutWrapkeyCommand(objID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, delegated uint64, wrapkey []byte) (*CommandMessage, error) {
|
||
if len(label) > LabelLength {
|
||
return nil, errors.New("label is too long")
|
||
}
|
||
if len(label) < LabelLength {
|
||
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
|
||
}
|
||
switch algorithm {
|
||
case AlgorithmAES128CCMWrap:
|
||
if keyLen := len(wrapkey); keyLen != 16 {
|
||
return nil, errors.New("wrapkey is wrong length")
|
||
}
|
||
case AlgorithmAES192CCMWrap:
|
||
if keyLen := len(wrapkey); keyLen != 24 {
|
||
return nil, errors.New("wrapkey is wrong length")
|
||
}
|
||
case AlgorithmAES256CCMWrap:
|
||
if keyLen := len(wrapkey); keyLen != 32 {
|
||
return nil, errors.New("wrapkey is wrong length")
|
||
}
|
||
default:
|
||
return nil, errors.New("invalid algorithm")
|
||
}
|
||
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypePutWrapKey,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, objID)
|
||
payload.Write(label)
|
||
binary.Write(payload, binary.BigEndian, domains)
|
||
binary.Write(payload, binary.BigEndian, capabilities)
|
||
binary.Write(payload, binary.BigEndian, algorithm)
|
||
binary.Write(payload, binary.BigEndian, delegated)
|
||
payload.Write(wrapkey)
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreatePutAuthkeyCommand(objID uint16, label []byte, domains uint16, capabilities, delegated uint64, encKey, macKey []byte) (*CommandMessage, error) {
|
||
if len(label) > LabelLength {
|
||
return nil, errors.New("label is too long")
|
||
}
|
||
if len(label) < LabelLength {
|
||
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
|
||
}
|
||
algorithm := AlgorithmYubicoAESAuthentication
|
||
// TODO: support P256 Authentication when it is released
|
||
// https://github.com/Yubico/yubihsm-shell/blob/1c8e254603e72f3f39cf1c3910996dbfcdba2b12/lib/yubihsm.c#L3110
|
||
if len(encKey) != 16 {
|
||
return nil, errors.New("invalid encryption key length")
|
||
}
|
||
if len(macKey) != 16 {
|
||
return nil, errors.New("invalid mac key length")
|
||
}
|
||
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypePutAuthKey,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, objID)
|
||
payload.Write(label)
|
||
binary.Write(payload, binary.BigEndian, domains)
|
||
binary.Write(payload, binary.BigEndian, capabilities)
|
||
binary.Write(payload, binary.BigEndian, algorithm)
|
||
binary.Write(payload, binary.BigEndian, delegated)
|
||
payload.Write(encKey)
|
||
payload.Write(macKey)
|
||
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreatePutDerivedAuthenticationKeyCommand(objID uint16, label []byte, domains uint16, capabilities uint64, delegated uint64, password string) (*CommandMessage, error) {
|
||
authKey := authkey.NewFromPassword(password)
|
||
return CreatePutAuthkeyCommand(objID, label, domains, capabilities, delegated, authKey.GetEncKey(), authKey.GetMacKey())
|
||
}
|
||
|
||
func CreateSignAttestationCertCommand(keyObjID, attestationObjID uint16) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeAttestAsymmetric,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, keyObjID)
|
||
binary.Write(payload, binary.BigEndian, attestationObjID)
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
func CreateExportWrappedCommand(wrapObjID uint16, objType uint8, objID uint16) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeExportWrapped,
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, wrapObjID)
|
||
binary.Write(payload, binary.BigEndian, objType)
|
||
binary.Write(payload, binary.BigEndian, objID)
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|
||
|
||
// CreateImportWrappedCommand will import a wrapped/encrypted Object that was
|
||
// previously exported by an YubiHSM2 device. The imported object will retain
|
||
// its metadata (Object ID, Domains, Capabilities …etc), however, the object’s
|
||
// origin will be marked as imported instead of generated.
|
||
func CreateImportWrappedCommand(wrapObjID uint16, nonce, data []byte) (*CommandMessage, error) {
|
||
command := &CommandMessage{
|
||
CommandType: CommandTypeImportWrapped,
|
||
}
|
||
if len(nonce) != 13 {
|
||
return nil, errors.New("invalid nonce length")
|
||
}
|
||
|
||
payload := bytes.NewBuffer([]byte{})
|
||
binary.Write(payload, binary.BigEndian, wrapObjID)
|
||
payload.Write(nonce)
|
||
payload.Write(data)
|
||
command.Data = payload.Bytes()
|
||
|
||
return command, nil
|
||
}
|