FU Logo
  • Startseite
  • Kontakt
  • Impressum
  • Home
  • Listenauswahl
  • Anleitungen

Re: [linux-minidisc] "netmdcli send" does not break on errors

<-- thread -->
<-- date -->
  • From: Thomas Arp <manner.moe@gmx.de>
  • To: Michael Karcher <Michael.Karcher@fu-berlin.de>, linux-minidisc@lists.fu-berlin.de
  • Date: Tue, 20 Sep 2011 22:59:18 +0200
  • Subject: Re: [linux-minidisc] "netmdcli send" does not break on errors

Am 18/09/2011 17:50, schrieb Michael Karcher:
Am Sonntag, den 18.09.2011, 17:05 +0200 schrieb Thomas Arp:
The key generation/exchange mechanism should go into
libnetmd. Maybe we should provide something like netmd_start_session()
and netmd_end_session() externally which set up and clean a secure
session  inside libnetmd.
Indeed it should. If you are going to implement that, you might want to
take a look at the MDSession object in netmd/libnetmd.py. While I don't
want to imply this is for sure the best way to design the interface, at
least that interface seemed to work out quite nicely for the
downloadhack script and gave the impression that will work for more
complex implementations, too.

Regards,
   Michael Karcher

O.K., these are my changes until yet, Please take a look at it.

Thomas
From 3a5d0fa0f79d8f0a4cb4c8c95d95e7000c5288bb Mon Sep 17 00:00:00 2001
From: Thomas Arp <manner.moe@gmx.de>
Date: Tue, 20 Sep 2011 22:36:50 +0200
Subject: [PATCH] move key exchange mechanism from netmdcli to libnetmd

---
 libnetmd/common.c     |    8 +-
 libnetmd/error.h      |    6 +-
 libnetmd/libnetmd.c   |    4 +
 libnetmd/libnetmd.h   |    2 +
 libnetmd/libnetmd.pro |    6 +-
 libnetmd/log.h        |    3 +
 libnetmd/secure.c     |    2 +-
 libnetmd/secure.h     |    2 +
 libnetmd/session.c    |  362 +++++++++++++++++++++++++++++++++++++++++++++++++
 libnetmd/session.h    |   81 +++++++++++
 netmdcli/netmdcli.c   |  206 ++++++----------------------
 11 files changed, 510 insertions(+), 172 deletions(-)
 create mode 100644 libnetmd/session.c
 create mode 100644 libnetmd/session.h

diff --git a/libnetmd/common.c b/libnetmd/common.c
index 51f19f3..994154c 100644
--- a/libnetmd/common.c
+++ b/libnetmd/common.c
@@ -102,8 +102,8 @@ int netmd_send_message(netmd_dev_handle *devh, unsigned char *cmd,
     }
 
     /* send data */
-    netmd_log(NETMD_LOG_DEBUG, "Command:\n");
-    netmd_log_hex(NETMD_LOG_DEBUG, cmd, cmdlen);
+    netmd_log(NETMD_LOG_TRACE, "Command:\n");
+    netmd_log_hex(NETMD_LOG_TRACE, cmd, cmdlen);
     if (libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR |
                         LIBUSB_RECIPIENT_INTERFACE, 0x80, 0, 0, cmd, (int)cmdlen,
                         NETMD_SEND_TIMEOUT) < 0) {
@@ -137,8 +137,8 @@ int netmd_recv_message(netmd_dev_handle *devh, unsigned char* rsp)
         return NETMDERR_USB;
     }
 
-    netmd_log(NETMD_LOG_DEBUG, "Response:\n");
-    netmd_log_hex(NETMD_LOG_DEBUG, rsp, (size_t)len);
+    netmd_log(NETMD_LOG_TRACE, "Response:\n");
+    netmd_log_hex(NETMD_LOG_TRACE, rsp, (size_t)len);
 
     /* return length */
     return len;
diff --git a/libnetmd/error.h b/libnetmd/error.h
index 6bbdfa8..d6c4034 100644
--- a/libnetmd/error.h
+++ b/libnetmd/error.h
@@ -24,7 +24,11 @@ typedef enum {
     NETMD_COMMAND_FAILED_REJECTED,
     NETMD_COMMAND_FAILED_UNKNOWN_ERROR,
 
-    NETMD_DES_ERROR
+    NETMD_DES_ERROR,
+
+    NETMD_SESSION_FAILED,
+    NETMD_SESSION_NOT_INITIALIZED,
+    NETMD_SESSION_TRANSFER_FAILED
 
 } netmd_error;
 
diff --git a/libnetmd/libnetmd.c b/libnetmd/libnetmd.c
index 0291cbe..beba7bd 100644
--- a/libnetmd/libnetmd.c
+++ b/libnetmd/libnetmd.c
@@ -303,6 +303,7 @@ static void set_group_data(minidisc* md, const int group, const char* const name
 int netmd_initialize_disc_info(netmd_dev_handle* devh, minidisc* md)
 {
     int disc_size = 0;
+    int flags = 0;
     char disc[256];
 
     md->group_count = get_group_count(devh);
@@ -329,6 +330,9 @@ int netmd_initialize_disc_info(netmd_dev_handle* devh, minidisc* md)
         set_group_data(md, 0, "<Untitled>", 0, 0);
     }
 
+    flags = netmd_get_disc_flags(devh);
+    md->writable = ((flags & NETMD_DISC_FLAG_WRITE_PROTECTED) ? 0 : 1);
+
     return disc_size;
 }
 
diff --git a/libnetmd/libnetmd.h b/libnetmd/libnetmd.h
index 52f7edb..9e33900 100644
--- a/libnetmd/libnetmd.h
+++ b/libnetmd/libnetmd.h
@@ -42,6 +42,7 @@
 #include "secure.h"
 #include "netmd_dev.h"
 #include "trackinformation.h"
+#include "session.h"
 
 /**
    Data about a group, start track, finish track and name. Used to generate disc
@@ -81,6 +82,7 @@ typedef struct {
     size_t header_length;
     struct netmd_group *groups;
     unsigned int group_count;
+    int writable;
 } minidisc;
 
 
diff --git a/libnetmd/libnetmd.pro b/libnetmd/libnetmd.pro
index d7c0af6..ee3c56b 100644
--- a/libnetmd/libnetmd.pro
+++ b/libnetmd/libnetmd.pro
@@ -5,8 +5,10 @@ CONFIG += staticlib link_pkgconfig create_prl console debug_and_release_target
 DEFINES += G_DISABLE_DEPRECATED=1
 
 PKGCONFIG += libusb-1.0
-HEADERS += common.h const.h error.h libnetmd.h log.h netmd_dev.h playercontrol.h secure.h trackinformation.h utils.h
-SOURCES += common.c error.c libnetmd.c log.c netmd_dev.c playercontrol.c secure.c trackinformation.c utils.c
+HEADERS += common.h const.h error.h libnetmd.h log.h netmd_dev.h playercontrol.h secure.h trackinformation.h utils.h \
+    session.h
+SOURCES += common.c error.c libnetmd.c log.c netmd_dev.c playercontrol.c secure.c trackinformation.c utils.c \
+    session.c
 LIBS    += -lusb-1.0 -lgcrypt
 
 mac:INCLUDEPATH += /opt/local/include
diff --git a/libnetmd/log.h b/libnetmd/log.h
index 9a21688..a72bca5 100644
--- a/libnetmd/log.h
+++ b/libnetmd/log.h
@@ -18,6 +18,9 @@ typedef enum {
         /** messages to display */
         NETMD_LOG_DEBUG,
 
+        /** trace usb commands and response */
+        NETMD_LOG_TRACE,
+
         /** Not a log level. Should only be used to display all messages. Should
             be the level with the highest value. */
         NETMD_LOG_ALL
diff --git a/libnetmd/secure.c b/libnetmd/secure.c
index a940d9d..9314d3b 100644
--- a/libnetmd/secure.c
+++ b/libnetmd/secure.c
@@ -372,7 +372,7 @@ void netmd_transfer_song_packets(netmd_dev_handle *dev,
 
         /* ... send it */
         error = libusb_bulk_transfer((libusb_device_handle*)dev, 2, packet, (int)packet_size, &transferred, 10000);
-        netmd_log(NETMD_LOG_DEBUG, "%d %d\n", packet_size, error);
+        netmd_log(NETMD_LOG_DEBUG, "Packets transferred: %d %s\n", packet_size, strerror(error));
 
         /* cleanup */
         free(packet);
diff --git a/libnetmd/secure.h b/libnetmd/secure.h
index 41e41ce..c9c7c89 100644
--- a/libnetmd/secure.h
+++ b/libnetmd/secure.h
@@ -189,6 +189,8 @@ netmd_error netmd_secure_get_track_uuid(netmd_dev_handle *dev, uint16_t track,
 netmd_error netmd_secure_delete_track(netmd_dev_handle *dev, uint16_t track,
                                       unsigned char *signature);
 
+size_t netmd_get_frame_size(netmd_wireformat wireformat);
+
 netmd_error netmd_prepare_packets(unsigned char* data, size_t data_lenght,
                                   netmd_track_packets **packets,
                                   size_t *packet_count,
diff --git a/libnetmd/session.c b/libnetmd/session.c
new file mode 100644
index 0000000..b75c531
--- /dev/null
+++ b/libnetmd/session.c
@@ -0,0 +1,362 @@
+/*
+ * session.c
+ *
+ * This file is part of libnetmd, a library for accessing Sony NetMD devices.
+ *
+ * Copyright (C) 2004 Bertrik Sikken
+ * Copyright (C) 2011 Alexander Sulfrian
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gcrypt.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "libnetmd.h"
+#include "utils.h"
+
+static const unsigned char ekb_chain[] = {0x25, 0x45, 0x06, 0x4d, 0xea, 0xca, 0x14, 0xf9,
+                                          0x96, 0xbd, 0xc8, 0xa4, 0x06, 0xc2, 0x2b, 0x81,
+                                          0x49, 0xba, 0xf0, 0xdf, 0x26, 0x9d, 0xb7, 0x1d,
+                                          0x49, 0xba, 0xf0, 0xdf, 0x26, 0x9d, 0xb7, 0x1d};
+
+static const unsigned char ekb_signature[] = {0xe8, 0xef, 0x73, 0x45, 0x8d, 0x5b, 0x8b, 0xf8,
+                                              0xe8, 0xef, 0x73, 0x45, 0x8d, 0x5b, 0x8b, 0xf8,
+                                              0x38, 0x5b, 0x49, 0x36, 0x7b, 0x42, 0x0c, 0x58};
+
+static unsigned char rootkey[] = {0x13, 0x37, 0x13, 0x37, 0x13, 0x37, 0x13, 0x37,
+                                  0x13, 0x37, 0x13, 0x37, 0x13, 0x37, 0x13, 0x37};
+
+static void build_ekb(netmd_ekb *ekb, netmd_keychain *keychain, netmd_keychain *next)
+{
+    size_t done;
+
+    /* build ekb */
+    ekb->id = 0x26422642;
+    ekb->depth = 9;
+    ekb->signature = malloc(sizeof(ekb_signature));
+    memcpy(ekb->signature, ekb_signature, sizeof(ekb_signature));
+
+    /* build ekb key chain */
+    ekb->chain = NULL;
+    for (done = 0; done < sizeof(ekb_chain); done+=16U)
+    {
+        next = malloc(sizeof(netmd_keychain));
+        if (ekb->chain == NULL) {
+            ekb->chain = next;
+        }
+        else {
+            keychain->next = next;
+        }
+        next->next = NULL;
+
+        next->key = malloc(16);
+        memcpy(next->key, ekb_chain + done, 16);
+
+        keychain = next;
+    }
+}
+
+static void ekb_cleanup(netmd_ekb *ekb, netmd_keychain *keychain, netmd_keychain *next)
+{
+    free(ekb->signature);
+    keychain = ekb->chain;
+    while (keychain != NULL) {
+        next = keychain->next;
+        free(keychain->key);
+        free(keychain);
+        keychain = next;
+    }
+}
+
+static void retailmac(unsigned char *rootkey, unsigned char *hostnonce,
+                      unsigned char *devnonce, unsigned char *sessionkey)
+{
+    gcry_cipher_hd_t handle1;
+    gcry_cipher_hd_t handle2;
+
+    unsigned char des3_key[24] = { 0 };
+    unsigned char iv[8] = { 0 };
+
+    gcry_cipher_open(&handle1, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+    gcry_cipher_setkey(handle1, rootkey, 8);
+    gcry_cipher_encrypt(handle1, iv, 8, hostnonce, 8);
+
+    memcpy(des3_key, rootkey, 16);
+    memcpy(des3_key+16, rootkey, 8);
+    gcry_cipher_open(&handle2, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0);
+    gcry_cipher_setkey(handle2, des3_key, 24);
+    gcry_cipher_setiv(handle2, iv, 8);
+    gcry_cipher_encrypt(handle2, sessionkey, 8, devnonce, 8);
+
+    gcry_cipher_close(handle1);
+    gcry_cipher_close(handle2);
+}
+
+int netmd_get_disc_flags(netmd_dev_handle* dev)
+{
+    int ret = -1;
+    unsigned char disc_flags_request[] = {0x00, 0x18, 0x06, 0x01, 0x10, 0x10,
+                                          0x00, 0xff, 0x00, 0x00, 0x01, 0x00,
+                                          0x0b  };
+    unsigned char response[14];
+
+    ret = netmd_exch_message(dev, disc_flags_request, 0x0d, response);
+    if(ret < 0 || response[0] != NETMD_STATUS_ACCEPTED)
+    {
+        netmd_log(NETMD_LOG_WARNING, "netmd_get_disc_flags failed\n");
+        return 0;
+    }
+
+    netmd_log(NETMD_LOG_DEBUG, "netmd_get_disc_flags succeeded, disc flags: 0x%02x\n", response[13]);
+    return response[13] & 0xff;
+}
+
+uint16_t netmd_get_recording_parameters(netmd_dev_handle *dev)
+{
+    int ret = -1;
+    char *enc;
+    unsigned char rec_param_request[] = {0x00, 0x18, 0x09, 0x80, 0x01, 0x03, 0x30, 0x88,
+                                         0x01, 0x00, 0x30, 0x88, 0x05, 0x00, 0x30, 0x88,
+                                         0x07, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00};
+    unsigned char response[38];
+
+    ret = netmd_exch_message(dev, rec_param_request, 0x18, response);
+    if(ret < 0 || response[0] != NETMD_STATUS_ACCEPTED)
+    {
+        netmd_log(NETMD_LOG_WARNING, "netmd_get_recording_parameters failed\n");
+        return 0;
+    }
+
+    /* following switch case clause not really needed, just for debug output */
+    switch (response[34]) {
+    case NETMD_ENCODING_SP :
+        enc = ((response[35] & 1) ? "SP MONO" : "SP STEREO");
+        break;
+    case NETMD_ENCODING_LP2 :
+        enc = "LP2";
+        break;
+    case NETMD_ENCODING_LP4 :
+        enc = "LP4";
+        break;
+    default : enc = "unknown";
+    }
+
+    netmd_log(NETMD_LOG_DEBUG, "netmd_get_recording_parameters succeeded, encoding: %s\n", enc);
+    return (response[34] << 8) + response[35];;
+}
+
+netmd_error netmd_download_possible(netmd_dev_handle *dev, const char *file, netmd_wireformat wireformat)
+{
+    size_t dicsflags;
+    /* uint16_t param;
+    netmd_disc_capacity cap; */
+
+    /* check if disc is write protected  */
+    dicsflags = netmd_get_disc_flags(dev);
+    if (dicsflags & NETMD_DISC_FLAG_WRITE_PROTECTED)
+    {
+        netmd_log(NETMD_LOG_ERROR, "Cannot download track, disc is write protected\n");
+        return 0;
+    }
+
+    /* TODO: check if enough space is available on the disc
+     * - calculate available space from cap.available and current netmd recording format
+     * - calculete space needed from file and wireformat
+     */
+    /*param = netmd_get_recording_parameters(dev);
+    netmd_get_disc_capacity(devh, &cap); */
+
+    netmd_log(NETMD_LOG_DEBUG, "Track download possible\n");
+    return 1;
+}
+
+netmd_error netmd_start_session(netmd_session *session, netmd_dev_handle *dev)
+{
+    netmd_error error;
+    netmd_loglevel level;
+    netmd_ekb ekb;
+    netmd_keychain *keychain = NULL;
+    netmd_keychain *next = NULL;
+    unsigned char hostnonce[8] = { 0 };
+    unsigned char devnonce[8] = { 0 };
+
+
+    session->devh = dev;
+    session->active = 0;
+
+    if(session->devh == NULL)
+    {
+        netmd_log(NETMD_LOG_ERROR, "No NetMD device selected\n");
+        return NETMD_SESSION_FAILED;
+    }
+
+    netmd_secure_session_key_forget(session->devh);
+    netmd_secure_leave_session(session->devh);
+
+    error = netmd_secure_set_track_protection(session->devh, 0x01);
+    if(error)
+        netmd_log(NETMD_LOG_WARNING, "netmd_secure_set_track_protection not supported on this device\n");
+    else
+        netmd_log(NETMD_LOG_DEBUG, "netmd_secure_set_track_protection succeeded\n");
+
+    error = netmd_secure_enter_session(session->devh);
+    level = (error ? NETMD_LOG_ERROR : NETMD_LOG_DEBUG);
+    netmd_log(level, "netmd_secure_enter_session: %s\n", netmd_strerror(error));
+    if(error)
+        return NETMD_SESSION_FAILED;
+
+    build_ekb(&ekb, keychain, next);
+
+    error = netmd_secure_send_key_data(session->devh, &ekb);
+    level = (error ? NETMD_LOG_ERROR : NETMD_LOG_DEBUG);
+    netmd_log(level, "netmd_secure_send_key_data: %s\n", netmd_strerror(error));
+    if(error)
+    {
+        netmd_leave_session(session);
+        return NETMD_SESSION_FAILED;
+    }
+
+    ekb_cleanup(&ekb, keychain, next);
+
+    /* exchange nonces */
+    gcry_create_nonce(hostnonce, sizeof(hostnonce));
+    error = netmd_secure_session_key_exchange(session->devh, hostnonce, devnonce);
+    level = (error ? NETMD_LOG_ERROR : NETMD_LOG_DEBUG);
+    netmd_log(level, "netmd_secure_session_key_exchange: %s\n", netmd_strerror(error));
+    if(error)
+    {
+        netmd_leave_session(session);
+        return NETMD_SESSION_FAILED;
+    }
+
+    /* calculate session key */
+    retailmac(rootkey, hostnonce, devnonce, session->sessionkey);
+
+    session->active = 1;
+    netmd_log(NETMD_LOG_DEBUG, "netmd_start_session succeeded\n");
+    return NETMD_NO_ERROR;
+}
+
+void netmd_leave_session(netmd_session * session)
+{
+    netmd_error error;
+    netmd_loglevel level;
+
+    error = netmd_secure_session_key_forget(session->devh);
+    level = (error ? NETMD_LOG_WARNING : NETMD_LOG_DEBUG);
+    netmd_log(level, "netmd_secure_session_key_forget: %s\n", netmd_strerror(error));
+
+    /* leave session */
+    error = netmd_secure_leave_session(session->devh);
+    level = (error ? NETMD_LOG_WARNING : NETMD_LOG_DEBUG);
+    netmd_log(level, "netmd_secure_session_key_forget: %s\n", netmd_strerror(error));
+
+    session->active = 0;
+    memset(session->sessionkey, 0x0, 8);
+    session->devh = NULL;
+    netmd_log(NETMD_LOG_DEBUG, "netmd_leave_session succeeded\n");
+}
+
+netmd_error netmd_send_track(netmd_session *session, const char *file, const char *title)
+{
+    FILE *f;
+    netmd_error error;
+    netmd_loglevel level;
+    netmd_track_packets *packets = NULL;
+    size_t packet_count = 0;
+    struct stat stat_buf;
+    unsigned char *data;
+    size_t data_size;
+    size_t frames = 0;
+    uint16_t track;
+    unsigned char uuid[8] = { 0 };
+    unsigned char new_contentid[20] = { 0 };
+    unsigned char kek[] = { 0x14, 0xe3, 0x83, 0x4e, 0xe2, 0xd3, 0xcc, 0xa5 };
+    unsigned char contentid[] = { 0x01, 0x0F, 0x50, 0x00, 0x00, 0x04, 0x00, 0x00,
+                                  0x00, 0x48, 0xA2, 0x8D, 0x3E, 0x1A, 0x3B, 0x0C,
+                                  0x44, 0xAF, 0x2f, 0xa0 };
+
+    if(!session->active)
+    {
+        netmd_log(NETMD_LOG_ERROR, "NetMD session not initialized \n");
+        return NETMD_SESSION_NOT_INITIALIZED;
+    }
+
+    error = netmd_secure_setup_download(session->devh, contentid, kek, session->sessionkey);
+    level = (error ? NETMD_LOG_ERROR : NETMD_LOG_DEBUG);
+    netmd_log(level, "netmd_secure_setup_download: %s\n", netmd_strerror(error));
+    if(error)
+        return NETMD_SESSION_TRANSFER_FAILED;
+
+    /* read source */
+    stat(file, &stat_buf);
+    data_size = (size_t)stat_buf.st_size;
+    data = malloc(data_size);
+    f = fopen(file, "rb");
+    fseek(f, 60, SEEK_CUR);
+    fread(data, data_size - 60, 1, f);
+    fclose(f);
+    frames = (data_size - 60) / 192;
+
+    error = netmd_prepare_packets(data, data_size-60, &packets, &packet_count, kek);
+    level = (error ? NETMD_LOG_ERROR : NETMD_LOG_DEBUG);
+    netmd_log(level, "netmd_prepare_packets: %s\n", netmd_strerror(error));
+    if(error)
+        return NETMD_SESSION_TRANSFER_FAILED;
+
+    /* send to device */
+    error = netmd_secure_send_track(session->devh, NETMD_WIREFORMAT_LP2,
+                                    NETMD_DISKFORMAT_LP2,
+                                    frames, packets,
+                                    packet_count, session->sessionkey,
+                                    &track, uuid, new_contentid);
+    level = (error ? NETMD_LOG_ERROR : NETMD_LOG_DEBUG);
+    netmd_log(level, "netmd_secure_send_track: %s\n", netmd_strerror(error));
+
+    /* cleanup */
+    netmd_cleanup_packets(&packets);
+
+    if(error)
+        return NETMD_SESSION_TRANSFER_FAILED;
+
+    /* set title */
+    netmd_log(NETMD_LOG_DEBUG, "New Track: %d\n", track);
+    netmd_cache_toc(session->devh);
+    if(!netmd_set_title(session->devh, track, title))
+        netmd_log(NETMD_LOG_WARNING, "netmd_set_title failed\n");
+    else
+        netmd_log(NETMD_LOG_DEBUG, "netmd_set_title \"%s\" suceeded\n", title);
+    netmd_sync_toc(session->devh);
+
+    /* commit track */
+    error = netmd_secure_commit_track(session->devh, track, session->sessionkey);
+    level = (error ? NETMD_LOG_ERROR : NETMD_LOG_DEBUG);
+    netmd_log(level, "netmd_secure_commit_track: %s\n", netmd_strerror(error));
+    if(error)
+        return NETMD_SESSION_TRANSFER_FAILED;
+
+    netmd_log(NETMD_LOG_DEBUG, "netmd_send_track succeeded\n");
+    return NETMD_NO_ERROR;
+}
+
+netmd_error netmd_recieve_track(netmd_session *session, size_t track, const char * file)
+{
+    /* TODO: implement track upload */
+    return NETMD_NOT_IMPLEMENTED;
+}
+
diff --git a/libnetmd/session.h b/libnetmd/session.h
new file mode 100644
index 0000000..7d57d10
--- /dev/null
+++ b/libnetmd/session.h
@@ -0,0 +1,81 @@
+#ifndef LIBNETMD_SESSION_H
+#define LIBNETMD_SESSION_H
+
+#include <stdio.h>
+
+#include "common.h"
+#include "error.h"
+#include "secure.h"
+
+typedef struct netmd_session {
+    netmd_dev_handle *devh;
+    unsigned char sessionkey[8];
+    int active;
+} netmd_session;
+
+/**
+   Get disc flags to check if disc is write protected.
+
+   @param dev pointer to device returned by netmd_open
+   @return bitfield representing the disc flags, see NETMD_DISC_FLAG_* constants,
+           0 on error
+*/
+int netmd_get_disc_flags(netmd_dev_handle *dev);
+
+/**
+   Get recording parameters.
+
+   @param dev pointer to device returned by netmd_open
+   @return current encoding the device uses for recording, see NETMD_ENCODING_*
+           and NETMD_CHANNELS_* constants, 0 on error
+*/
+uint16_t netmd_get_recording_parameters(netmd_dev_handle *dev);
+
+/**
+   Check if download is possible, disc write protected? enough free space ?.
+
+   @param dev pointer to device returned by netmd_open
+   @param file audio file to download
+   @param wireformat wireformat to be used on the disc
+   @return 1 if download possible, 0 if not
+*/
+netmd_error netmd_download_possible(netmd_dev_handle *dev, const char *file,
+                                             netmd_wireformat wireformat);
+
+/**
+   Sets up a secure session including key exchange mechanism.
+
+   @param session pointer to a netmd_session struct
+   @param dev pointer to device returned by netmd_open
+   @return NETMD_NO_ERROR on success else NETMD_SESSION_FAILED
+*/
+netmd_error netmd_start_session(netmd_session *session, netmd_dev_handle *dev);
+
+/**
+   Leaves a secure session.
+
+   @param session pointer to a netmd_session struct
+*/
+void netmd_leave_session(netmd_session *session);
+
+/**
+   Send a track to the device usind a secure session.
+
+   @param session pointer to a netmd_session struct initialized by netmd_start_session
+   @param file audio file to download
+   @param title title of the track to be stored
+   @return NETMD_NO_ERROR on success else error code of the last failed function
+*/
+netmd_error netmd_send_track(netmd_session *session, const char *file, const char *title);
+
+/**
+   Recieve a track from the device usind a secure session (Sony MZ-RH1 only).
+
+   @param session pointer to a netmd_session struct initialized by netmd_start_session
+   @param track track number
+   @param file audio filename to store the track
+   @return NETMD_NO_ERROR on success else error code of the last failed function
+*/
+netmd_error netmd_recieve_track(netmd_session *session, size_t track, const char * file);
+
+#endif
diff --git a/netmdcli/netmdcli.c b/netmdcli/netmdcli.c
index 60f1d81..6dd3a8a 100644
--- a/netmdcli/netmdcli.c
+++ b/netmdcli/netmdcli.c
@@ -20,6 +20,7 @@
 
 #include <gcrypt.h>
 #include <unistd.h>
+#include <sys/stat.h>
 
 #include "libnetmd.h"
 #include "utils.h"
@@ -144,30 +145,6 @@ void print_time(const netmd_time *time)
     printf("%02d:%02d:%02d.%02d", time->hour, time->minute, time->second, time->frame);
 }
 
-void retailmac(unsigned char *rootkey, unsigned char *hostnonce,
-               unsigned char *devnonce, unsigned char *sessionkey)
-{
-    gcry_cipher_hd_t handle1;
-    gcry_cipher_hd_t handle2;
-
-    unsigned char des3_key[24] = { 0 };
-    unsigned char iv[8] = { 0 };
-
-    gcry_cipher_open(&handle1, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
-    gcry_cipher_setkey(handle1, rootkey, 8);
-    gcry_cipher_encrypt(handle1, iv, 8, hostnonce, 8);
-
-    memcpy(des3_key, rootkey, 16);
-    memcpy(des3_key+16, rootkey, 8);
-    gcry_cipher_open(&handle2, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0);
-    gcry_cipher_setkey(handle2, des3_key, 24);
-    gcry_cipher_setiv(handle2, iv, 8);
-    gcry_cipher_encrypt(handle2, sessionkey, 8, devnonce, 8);
-
-    gcry_cipher_close(handle1);
-    gcry_cipher_close(handle2);
-}
-
 int main(int argc, char* argv[])
 {
     netmd_dev_handle* devh;
@@ -175,9 +152,9 @@ int main(int argc, char* argv[])
     netmd_device *device_list, *netmd;
     unsigned int i = 0;
     unsigned int j = 0;
-    char name[16];
+    unsigned char name[16];
     uint16_t track, playmode;
-    int c;
+    int c, l;
     netmd_time time;
     netmd_error error;
     FILE *f;
@@ -212,20 +189,41 @@ int main(int argc, char* argv[])
     printf("%s\n", name);
 
     netmd_initialize_disc_info(devh, md);
-    printf("Disc Title: %s\n\n", md->groups[0].name);
+    printf("Disc Title: %s\n", md->groups[0].name);
+    printf("Protection status: %s\n\n", (md->writable ? "writable" : "write protected"));
 
     /* by default, log only errors */
     netmd_set_log_level(NETMD_LOG_ERROR);
 
     /* parse options */
     while (1) {
-        c = getopt(argc, argv, "t");
+        c = getopt(argc, argv, "v");
         if (c == -1) {
             break;
         }
         switch (c) {
-        case 't':
-            netmd_set_log_level(NETMD_LOG_ALL);
+        case 'v':
+            l = argv[optind][0];
+            switch (l) {
+            case '0' :
+                netmd_set_log_level(NETMD_LOG_NONE);
+                break;
+            case '1' :
+                netmd_set_log_level(NETMD_LOG_ERROR);
+                break;
+            case '2' :
+                netmd_set_log_level(NETMD_LOG_WARNING);
+                break;
+            case '3' :
+                netmd_set_log_level(NETMD_LOG_DEBUG);
+                break;
+            case '4' :
+                netmd_set_log_level(NETMD_LOG_ALL);
+                break;
+            default:
+                fprintf(stderr, "Unknown verbosity level '%i'\n", l);
+                break;
+            }
             break;
         default:
             fprintf(stderr, "Unknown option '%c'\n", c);
@@ -234,8 +232,8 @@ int main(int argc, char* argv[])
     }
 
     /* update argv and argc after parsing options */
-    argv = &argv[optind - 1];
-    argc -= (optind - 1);
+    argv = &argv[optind];
+    argc -= (optind);
 
     /* parse commands */
     if(argc > 1)
@@ -405,139 +403,14 @@ int main(int argc, char* argv[])
             fclose(f);
         }
         else if (strcmp("send", argv[1]) == 0) {
-            netmd_error error;
-            netmd_ekb ekb;
-            unsigned char chain[] = {0x25, 0x45, 0x06, 0x4d, 0xea, 0xca,
-                                     0x14, 0xf9, 0x96, 0xbd, 0xc8, 0xa4,
-                                     0x06, 0xc2, 0x2b, 0x81, 0x49, 0xba,
-                                     0xf0, 0xdf, 0x26, 0x9d, 0xb7, 0x1d,
-                                     0x49, 0xba, 0xf0, 0xdf, 0x26, 0x9d,
-                                     0xb7, 0x1d};
-            unsigned char signature[] = {0xe8, 0xef, 0x73, 0x45, 0x8d, 0x5b,
-                                         0x8b, 0xf8, 0xe8, 0xef, 0x73, 0x45,
-                                         0x8d, 0x5b, 0x8b, 0xf8, 0x38, 0x5b,
-                                         0x49, 0x36, 0x7b, 0x42, 0x0c, 0x58};
-            unsigned char rootkey[] = {0x13, 0x37, 0x13, 0x37, 0x13, 0x37,
-                                       0x13, 0x37, 0x13, 0x37, 0x13, 0x37,
-                                       0x13, 0x37, 0x13, 0x37};
-            netmd_keychain *keychain;
-            netmd_keychain *next;
-            size_t done;
-            unsigned char hostnonce[8] = { 0 };
-            unsigned char devnonce[8] = { 0 };
-            unsigned char sessionkey[8] = { 0 };
-            unsigned char kek[] = { 0x14, 0xe3, 0x83, 0x4e, 0xe2, 0xd3, 0xcc, 0xa5 };
-            unsigned char contentid[] = { 0x01, 0x0F, 0x50, 0x00, 0x00, 0x04,
-                                          0x00, 0x00, 0x00, 0x48, 0xA2, 0x8D,
-                                          0x3E, 0x1A, 0x3B, 0x0C, 0x44, 0xAF,
-                                          0x2f, 0xa0 };
-            netmd_track_packets *packets = NULL;
-            size_t packet_count = 0;
-            struct stat stat_buf;
-            unsigned char *data;
-            size_t data_size;
-
-            uint16_t track;
-            unsigned char uuid[8] = { 0 };
-            unsigned char new_contentid[20] = { 0 };
-
-            error = netmd_secure_leave_session(devh);
-            puts(netmd_strerror(error));
-
-            error = netmd_secure_set_track_protection(devh, 0x01);
-            puts(netmd_strerror(error));
-
-            error = netmd_secure_enter_session(devh);
-            puts(netmd_strerror(error));
-
-            /* build ekb */
-            ekb.id = 0x26422642;
-            ekb.depth = 9;
-            ekb.signature = malloc(sizeof(signature));
-            memcpy(ekb.signature, signature, sizeof(signature));
-
-            /* build ekb key chain */
-            ekb.chain = NULL;
-            for (done = 0; done < sizeof(chain); done+=16U)
-            {
-                next = malloc(sizeof(netmd_keychain));
-                if (ekb.chain == NULL) {
-                    ekb.chain = next;
-                }
-                else {
-                    keychain->next = next;
-                }
-                next->next = NULL;
-
-                next->key = malloc(16);
-                memcpy(next->key, chain + done, 16);
-
-                keychain = next;
-            }
+            netmd_session session;
 
-            error = netmd_secure_send_key_data(devh, &ekb);
-            puts(netmd_strerror(error));
-
-            /* cleanup */
-            free(ekb.signature);
-            keychain = ekb.chain;
-            while (keychain != NULL) {
-                next = keychain->next;
-                free(keychain->key);
-                free(keychain);
-                keychain = next;
+            if(netmd_download_possible(devh, argv[2], NETMD_WIREFORMAT_LP2))
+            {
+                if(!netmd_start_session(&session, devh))
+                    netmd_send_track(&session, argv[2], "test");
+                netmd_leave_session(&session);
             }
-
-            /* exchange nonces */
-            gcry_create_nonce(hostnonce, sizeof(hostnonce));
-            error = netmd_secure_session_key_exchange(devh, hostnonce, devnonce);
-            puts(netmd_strerror(error));
-
-            /* calculate session key */
-            retailmac(rootkey, hostnonce, devnonce, sessionkey);
-
-            error = netmd_secure_setup_download(devh, contentid, kek, sessionkey);
-            puts(netmd_strerror(error));
-
-            /* read source */
-            stat(argv[2], &stat_buf);
-            data_size = (size_t)stat_buf.st_size;
-            data = malloc(data_size);
-            f = fopen(argv[2], "rb");
-            fseek(f, 60, SEEK_CUR);
-            fread(data, data_size - 60, 1, f);
-            fclose(f);
-            error = netmd_prepare_packets(data, data_size-60, &packets, &packet_count, kek);
-            puts(netmd_strerror(error));
-
-            /* send to device */
-            error = netmd_secure_send_track(devh, NETMD_WIREFORMAT_LP2,
-                                            NETMD_DISKFORMAT_LP2,
-                                            (data_size - 60) / 192, packets,
-                                            packet_count, sessionkey,
-                                            &track, uuid, new_contentid);
-            puts(netmd_strerror(error));
-
-            /* cleanup */
-            netmd_cleanup_packets(&packets);
-
-            /* set title */
-            netmd_log(NETMD_LOG_DEBUG, "New Track: %d\n", track);
-            netmd_cache_toc(devh);
-            netmd_set_title(devh, track, "test");
-            netmd_sync_toc(devh);
-
-            /* commit track */
-            error = netmd_secure_commit_track(devh, track, sessionkey);
-            puts(netmd_strerror(error));
-
-            /* forget key */
-            error = netmd_secure_session_key_forget(devh);
-            puts(netmd_strerror(error));
-
-            /* leave session */
-            error = netmd_secure_leave_session(devh);
-            puts(netmd_strerror(error));
         }
         else if(strcmp("help", argv[1]) == 0)
         {
@@ -757,7 +630,12 @@ void print_syntax()
     puts("\nNetMD test suite.");
     puts("Usage: netmd [options] command args");
     puts("Options:");
-    puts("      -t enable tracing of USB command and response data");
+    puts("      -v <level> set verbosity level in a range from 0 to 4");
+    puts("                 0 - do not display any message");
+    puts("                 1 - display fatal error messages (DEFAULT)");
+    puts("                 2 - display warning messages");
+    puts("                 3 - display other debug messages");
+    puts("                 4 - enable tracing of USB command and response data");
     puts("Commands:");
     puts("rename # <string> - rename track # to <string> track numbers are off by one (ie track 1 is 0)");
     puts("move #1 #2 - make track #1 track #2");
-- 
1.7.6.msysgit.0

<-- thread -->
<-- date -->
  • Follow-Ups:
    • Re: [linux-minidisc] "netmdcli send" does not break on errors
      • From: Michael Karcher <Michael.Karcher@fu-berlin.de>
  • References:
    • [linux-minidisc] "netmdcli send" does not break on errors
      • From: Thomas Arp <manner.moe@gmx.de>
    • Re: [linux-minidisc] "netmdcli send" does not break on errors
      • From: Thomas Arp <manner.moe@gmx.de>
    • Re: [linux-minidisc] "netmdcli send" does not break on errors
      • From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
    • Re: [linux-minidisc] "netmdcli send" does not break on errors
      • From: Thomas Arp <manner.moe@gmx.de>
    • Re: [linux-minidisc] "netmdcli send" does not break on errors
      • From: Michael Karcher <Michael.Karcher@fu-berlin.de>
  • linux-minidisc - September 2011 - Archives indexes sorted by:
    [ thread ] [ subject ] [ author ] [ date ]
  • Complete archive of the linux-minidisc mailing list
  • More info on this list...

Hilfe

  • FAQ
  • Dienstbeschreibung
  • ZEDAT Beratung
  • postmaster@lists.fu-berlin.de

Service-Navigation

  • Startseite
  • Listenauswahl

Einrichtung Mailingliste

  • ZEDAT-Portal
  • Mailinglisten Portal